I've really been digging Trigger.io. We're really rocking our app. There are few areas that Trigger could definitely improve. Debug logging is one of those. They have their own tool called Catalyst which exports the developer console of the simulator's app to your browser. I haven't been able to get that to work with out AngularJS app, so I'm stuck with the terminal output...which is pretty terrible. It's impossible to make sense of it since you're sifting through lines of noise to find the few bits that actually make sense. It's terrible. I won't event give you an example, because if you're using it, you know how bad it is.

So, let's fix it. We're using Yeoman and running out app outside of Trigger.io most of the time. We're also using AngularJS so we can inject some services for logging rather than calling the forge.logging.log directly. So let's create that service.

# I don't like having the forge namespace scattered everywhere
# just to do logging. This helps with that and lets us just use
# logging as an Angular service
angular.module('nb').service 'log', (forge) ->


  # we are exposing all the standard console logging....just don't abuse it. :)
  return console if forge.is.web()

  error = (args) ->
    args = Array.prototype.slice.call(args)
    allErrors = _.filter args, (o) -> o && o instanceof Error
    if allErrors
      f = _.first allErrors

  # Forge expects only two parameters to log, message and error. We need
  # to combine them into a single string. I don't want to give up being
  # able to pass objects to console.log since that is pretty useful for
  # inspecting them.
  message = (args) ->
    # arguments isn't a _real_ array and needs to be converted to one before
    # joining it as a string.
    #
    # Todo. It would be nice to format this message a bit. It jumbles together
    # in the console.
    Array.prototype.slice.call(args).toString()

  groups = []

  group = (name) ->
    log "▾ #{name}"
    groups.push name

  groupEnd = ->
    groups.pop()

  log = (message, error, method = forge.logging.log) ->
    padding = ""
    _.times groups.length, -> padding += '| '

    forge.logging.log "#{padding}#{message}", error


  log:      -> log message(arguments), error(arguments)
  debug:    -> log message(arguments), error(arguments), forge.logging.debug
  info:     -> log message(arguments), error(arguments), forge.logging.info
  warn:     -> log message(arguments), error(arguments), forge.logging.warn
  error:    -> log message(arguments), error(arguments), forge.logging.error
  critical: -> log message(arguments), error(arguments), forge.logging.critical
  group: group
  groupCollapsed: group
  groupEnd: groupEnd

So you might be wondering what group does. Let's just say it's awesome. Try it out. It does what you think it should do in the browser and we just recreated it a bit here. So now we can inject logging and call all the log methods we want.

Now to deal with the output. I create grunt-forge to wrap running forge in order to get it built into our Yeoman workflow. We just needed to tweak that a bit to strip out the bits we don't care about...most of the time. After a bit of tweaking, we end up with output that looks like this.

Running "forge:build" (forge) task
[   INFO] Forge tools running at version 3.3.37
[   INFO] Update result: you already have the latest tools
[   INFO] Checking JavaScript files...
[WARNING] /Users/markborcherding/source/natron/HeartChase-Mobile/src/components/angular/angular.js(60): SyntaxError: int is a reserved identifier
[WARNING]     msie              = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
[WARNING] ........................^
[WARNING] /Users/markborcherding/source/natron/HeartChase-Mobile/src/components/underscore/test/vendor/qunit.js(499): SyntaxError: invalid property id
[WARNING]   throws: function( block, expected, message ) {
[WARNING] ........^
[   INFO] JavaScript check complete
[   INFO] Verifying your configuration settings...
[   INFO] Configuration settings check complete
[   INFO] Development build created. Use forge run to run your app.

Running "forge:ios_sim" (forge) task
[   INFO] Forge tools running at version 3.3.37
[   INFO] Running iOS Simulator
[   INFO] Starting simulator
[   INFO] Showing log output:
[   INFO] Loading pusher service
[   INFO] Pusher : State changed : initialized -> connecting
[   INFO] Pusher : Connecting
[   INFO] Pusher : State changed : connecting -> connected
[   INFO] Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"zsuwjs"}}
[   INFO] Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"event_103"}}
[   INFO] Showing tab,map
[   INFO] Loading game event,[object Object]
[   INFO] Clearing existing markers
[   INFO] ▾ Map._drawEventMarkers
[   INFO] | Creating HQ Marker
[   INFO] | ▾ Drawing Challenge Markers
[   INFO] | | Drawing challenge marker,[object Object]
[   INFO] | | Drawing challenge marker,[object Object]
[   INFO] | ▾ Drawing donation markers
[   INFO] | | Drawing donation:,[object Object]
[   INFO] | | Drawing donation:,[object Object]
[   INFO] Showing tab,pics
[   INFO] Moving to the next step
[   INFO] Sharing photo
[   INFO] Submitting team photo,zsuwjs,
[  ERROR] Error: 'undefined' is not an object
[  ERROR] Stack trace:
[  ERROR] submitPhoto@file:///..../src/scripts/services.js:11
[  ERROR] sharePhoto@file:///..../src/scripts/controllers.js:124
[  ERROR] @file:///..../src/components/angular/angular.js:6262
[  ERROR] $eval@file:///..../src/components/angular/angular.js:7985
[  ERROR] f@file:///..../src/scripts/nb.js:208
[  ERROR] @file:///..../src/scripts/nb.js:212
[  ERROR] $eval@file:///..../src/components/angular/angular.js:7985
[  ERROR] $apply@file:///..../src/components/angular/angular.js:8063
[  ERROR] onTouch@file:///..../src/scripts/nb.js:211
[  ERROR] @file:///..../src/components/angular/angular.js:1927

That is some output that is much more useful to me. You can see a bit of the groups at work there too. I'm not entirely proud of grunt-forge as it exists now, but it's a good start and it makes Trigger's log so much more useful to me.

Yeoman has been great so far. So has Angular. I’m really enjoying working with both. One of the headache is Yeoman brakes from Angular’s convention of using Jasmine + Testacular and replaces it with Mocha. I don’t really have information to make an opinion of either, but Mocha has served me well up to testing the Angular stuff, so I decided to stick with that, but there was a few gotchas a long the way.

Upgrade to 1.1.x

Up until that point I was using the Angular Stable that comes with yeoman install angular. In order to get Mocha to work, you’ll need to upgrade to the unstable branch.

Include angular-mocks

You’ll need to include angular-mocks.js somewhere. I added it to test/lib and included it right before my spec files in test/index.html

<script src="lib/angular-mocks.js"></script>

Tweak angular-mocks

After the first two steps, you’d think it would work. So did I, but it didn’t I got the following error.

Running "mocha:all" (mocha) task
Testing index.htmlF
>> HomeController - true is true
>> Message: 'undefined' is not an object (evaluating 'currentSpec.queue.running')

<WARN> 1/2 assertions failed (0s) Use --force to continue. </WARN>

Aborted due to warnings.

After a ton of Googling, I found a Google Group Message with the same problem. Their solution, tweak the angular-mocks.js just a bit.

function isSpecRunning() {
    return currentSpec && currentSpec.queue && currentSpec.queue.running
        || currentSpec; // for Mocha.
}

Now everything works…for now.

Running "mocha:all" (mocha) task
Testing index.html..OK
>> 2 assertions passed (0s)

Done, without errors.

I may eventually switch back to testacular, but we’ll see how far we can go with this setup.

As I've been mentioning, I'm working on a Trigger.io project. Things have been going great except one thing. It copies files around the project directory as you build your project for multiple platforms. Here is what NERDTree looks like in my Vim.

▸ app/                   <-- My AngularJS app files
▸ bin/                   <-- Some scripts to help run other tasks
▾ development/           <-- Holds copies of the build src dir
  ▸ android/             <--  ....for android
  ▸ ios/                 <--    ....and ios
  ▸ web/                 <--      ....and the web
▸ node_modules/          <-- All the NodeJS bits I'm using
▸ release/               <-- Packaged apps for each platform
▸ src/                   <-- My actual source files
▸ test/                  <-- My Tests

So I care about three of those directories: app, src, and test. To some extent I care about bin, but the others I don't really care about. Now I don't use NERDTree that much. It's nice and useful, but the more useful plugin for me is CtrlP.

CtrlP is awesome. I love it, but with 4 copies of my source, I'm constantly selecting the wrong version of my file. I wanted to ignore them from both CtrlP and NERDTree, but I don't want to always ignore development. Luckily my buddy turned me onto a possible solution; Per-project .vimrc files.

There are plent of articles on the Google that will help you set it up, but here's what I did and is so far working great.

I added this to my ~/.vimrc:

set exrc            " enable per-directory .vimrc files
    set secure          " disable unsafe commands in local .vimrc files

Then added this to my .vimrc in the project directory:

let g:ctrlp_custom_ignore = '\v[\/](release|node_modules|development)$'           " Ignores for CtrlP
    let NERDTreeIgnore=['development$', 'node_modules$', 'release$', '\.vim$', '\~$'] " Ignores for NERDTree

That's it. It works great. CtrlP is now always getting the right file and NERDTree has a lot less noise.

I've been working on an AngularJS project lately and have really come to like it. While I'm at it, I'm giving CoffeScript a real try (again). AngularJS has been really great so far. It feels really light and doesn't take much to get up and running. It's still unknown if I'm doing it the right way or I'm mucking it all up, but things are working, are easy to read and test, so I really can't complain too much. I'm putting the patterns I find useful in MarkBorcherding/angular-seed.

My latest challenge was to make my CoffeeScript classes available inside my AngularJS controllers. I had a class with an embdedded class such as this:

controller 'MyCtrl', ($scope) ->

  class Map
    constructor: (data) ->
      # setup the map

    toggle_map_type: ->
      # do whatever

  map = new Map($scope.some_data)
  $scope.toggle_map_type = ->
    map.toggle_map_type()

So that does exactly what I want it to do, but the presence of the Map class in there clutters up its essence. I would like to move that out of the controller. I could just toss it in a top level file, maybe create a nice namespace for it, but Angular has the ability to inject services into my controller. Why not inject this class too?

Here's where I don't know if I'm jacking it all up. I'm not sure if this should be injected as a value or a service. It makes sense to me to be either since it is a constant...but happens to be a function. So here it goes.

I created a new factory service (using the helpers from my angular-seed project).

factory 'Map', ->
  class Map
    constructor: (data) ->
      # setup the map

    toggle_map_type: ->
      # do whatever

Now I can just inject it on the controller.

controller 'MyCtrl', ($scope, Map) ->

  map = new Map($scope.some_data)
  $scope.toggle_map_type = ->
    map.toggle_map_type()

Done. Is that actually a service? I don't want the constructor function invoked when it injects, so that's why I ended up making it a factory rather than a service. Right now it's working, and when testing the controller that class is easy to mock so I'm happy so far.

I've recently started working on a Trigger.io port of our native iOS app. Some of the workflow is a bit repetitive. To ease this I've started a new set of Rake tasks to make life a bit less painful. Introducing trigger-tasks.

Right now it is very much in flux. It is a dump of what my project's Rakefile looked like. Over time it will become more organized and useful.