modify

modify

=======
babaspec: Test framework for human and real-world.
========

test framework using babascript

- 人の挙動を記述するDSL Babascriptを利用したテストフレームワーク
- 人のスペックや実世界のスペックを記述し、テストできるようになる

## Background

- プログラムがどのように動作するべきかのスペックを記述するためにテストが存在する。
- テストを書くことはプログラムを開発していく上で非常に有用である。
- しかし、現状のテストはプログラムをテストすることしかできない。
- TODO: 実世界や人にテスト手法を適応する意味

- 人や実世界のスペックを定義し、それをテストできるような仕組みはない。
- そこで、本研究では、人や実世界のスペックを定義してテストできるようなテストフレームワーク Babaspec を提案する。

### 人や実世界をテストできると何が嬉しいの？

-

プログラムは、そのプログラムがどのように動作するべきかのスペックを記述し、テストする。
プログラムがテストできる対象はプログラムのみであるが、人や実世界という要素をもテストしたいはずだ。
しかし、人のスペックや実世界のスペックを定義し、テストできるような仕組みはない。
そこで、本研究では、人や実世界のスペックを定義してテストできるようなテストフレームワーク Babaspec を提案する。

## Babaspec

Babaspecは、人への行動命令をプログラム上で記述可能にする Babascript と node.js上で利用可能な
テストフレームワーク mocha を組み合わせる。

    describe "baba spec test", ->

      it "should return true", ->

        # 人間のスペックを記述する
        baba = new Babaspec "baba"

        baba.equal "プログラムが書けますか", true # format is boolean
        baba.notEqual "hoge", 'are' # format is string
        baba.lt "何歳ですか", 20 # format is number
        # fomat List の場合をどうするか
        baba.equal baba.選んでください({formatL list, list: [1,2,3]}), [1,2] # format is list?

        # 実世界のスペックを記述する
        world = new Babaspec "real-world", "name"
        world.equal "SFCのすぐ近く", true

## 議論

- 本当にテストしたと言い切れるのか？

## 関連研究

- 人とか実世界をテストするような仕組みある？

## おわりに

=======
babaspec: Test framework for human and real-world.
========

test framework using babascript

- 人の挙動を記述するDSL Babascriptを利用したテストフレームワーク
- 人のスペックや実世界のスペックを記述し、テストできるようになる

## Background

プログラムは、そのプログラムがどのように動作するべきかのスペックを記述し、テストする。
プログラムがテストできる対象はプログラムのみであるが、人や実世界という要素をもテストしたいはずだ。
しかし、人のスペックや実世界のスペックを定義し、テストできるような仕組みはない。
そこで、本研究では、人や実世界のスペックを定義してテストできるようなテストフレームワーク Babaspec を提案する。

## Babaspec

Babaspecは、人への行動命令をプログラム上で記述可能にする Babascript と node.js上で利用可能な
テストフレームワーク mocha を組み合わせる。

    describe "baba spec test", ->

      it "should return true", ->

        # 人間のスペックを記述する
        baba = new Babaspec "baba"

        baba.equal "プログラムが書けますか", true # format is boolean
        baba.notEqual "hoge", 'are' # format is string
        baba.lt "何歳ですか", 20 # format is number
        # fomat List の場合をどうするか
        baba.equal baba.選んでください({formatL list, list: [1,2,3]}), [1,2] # format is list?

        # 実世界のスペックを記述する
        world = new Babaspec "real-world", "name"
        world.equal "SFCのすぐ近く", true

## 議論

## 関連研究

- 人とか実世界をテストするような仕組みある？

## おわりに


Minor whitespaces change, clearer init of @farthest_right property

    Madul = require 'madul'

    class Carver extends Madul
      deps: [ 'config' ]

      starting_row: 0

      row:   0
      col:   0
      steps: 0

      previous_direction: undefined
      total_distance:     0
      farthest_right:
        row: 0
        col: 0

      directions:
        LEFT:  0
        RIGHT: 1
        UP:    2
        DOWN:  3

      clear_state: =>
        @row = @starting_row
        @col = 0

        @farthest_right     = row: @row, col: @col
        @previous_direction = undefined

        @steps          = 0
        @total_distance = 0

        for row, r in @config.grid
          for cell, c in @config.grid[r]
            @config.grid[r][c] = false

        @config.grid[@row][@col] = true

      update_steps: (dir) =>
        if @previous_direction?
          if @previous_direction == @directions[dir]
            ++@steps
          else
            @steps = 0
        else
          @previous_direction = @directions[dir]
          @steps = 0

      neighbor: (x = 0, y = 0) => @config.grid[@row + x][@col + y]

      carve_path: =>
        more_to_carve = true
        path          = [{ row: @row, col: @col }]

        while more_to_carve
          neighbors = [ ]
          width     = @config.grid[0].length
          height    = @config.grid.length

          neighbors.push 'UP'    if @row > 0          && @neighbor(-1,  0) == false
          neighbors.push 'DOWN'  if @row < height - 2 && @neighbor( 1,  0) == false
          neighbors.push 'LEFT'  if @col > 0          && @neighbor( 0, -1) == false
          neighbors.push 'RIGHT' if @col < width      && @neighbor( 0,  1) == false

          if neighbors.length == 0
            @col = @farthest_right.col
            @row = @farthest_right.row

            continue
          else
            direction = @config.pathfinder neighbors

            switch @directions[direction]
              when @directions.RIGHT then @config.grid[@row][++@col] = true
              when @directions.LEFT  then @config.grid[@row][--@col] = true
              when @directions.UP    then @config.grid[--@row][@col] = true
              when @directions.DOWN  then @config.grid[++@row][@col] = true
              when undefined         then continue

            @update_steps direction

            if @col > @farthest_right.col
              @farthest_right.col = @col
              @farthest_right.row = @row

            if @farthest_right.col < @config.grid[0].length - 1
              path.push row: @row, col: @col, direction: direction

              continue
            else
              more_to_carve = false

        path

    module.exports = Carver

    Madul = require 'madul'

    class Carver extends Madul
      deps: [ 'config' ]

      starting_row: 0

      row:   0
      col:   0
      steps: 0

      previous_direction: undefined
      total_distance:     0
      farthest_right:
        row: 0
        col: 0

      directions:
        LEFT:  0
        RIGHT: 1
        UP:    2
        DOWN:  3

      clear_state: =>
        @row  = @starting_row
        @col  = 0

        @farthest_right     = row: @row, col: 0
        @previous_direction = undefined

        @steps          = 0
        @total_distance = 0

        for row, r in @config.grid
          for cell, c in @config.grid[r]
            @config.grid[r][c] = false

        @config.grid[@row][@col] = true

      update_steps: (dir) =>
        if @previous_direction?
          if @previous_direction == @directions[dir]
            ++@steps
          else
            @steps = 0
        else
          @previous_direction = @directions[dir]
          @steps = 0

      neighbor: (x = 0, y = 0) => @config.grid[@row + x][@col + y]

      carve_path: =>
        more_to_carve = true
        path          = [{ row: @row, col: @col }]

        while more_to_carve
          neighbors = [ ]
          width     = @config.grid[0].length
          height    = @config.grid.length

          neighbors.push 'UP'    if @row > 0          && @neighbor(-1,  0) == false
          neighbors.push 'DOWN'  if @row < height - 2 && @neighbor( 1,  0) == false
          neighbors.push 'LEFT'  if @col > 0          && @neighbor( 0, -1) == false
          neighbors.push 'RIGHT' if @col < width      && @neighbor( 0,  1) == false

          if neighbors.length == 0
            @col = @farthest_right.col
            @row = @farthest_right.row

            continue
          else
            direction = @config.pathfinder neighbors

            switch @directions[direction]
              when @directions.RIGHT then @config.grid[@row][++@col] = true
              when @directions.LEFT  then @config.grid[@row][--@col] = true
              when @directions.UP    then @config.grid[--@row][@col] = true
              when @directions.DOWN  then @config.grid[++@row][@col] = true
              when undefined         then continue

            @update_steps direction

            if @col > @farthest_right.col
              @farthest_right.col = @col
              @farthest_right.row = @row

            if @farthest_right.col < @config.grid[0].length - 1
              path.push row: @row, col: @col, direction: direction

              continue
            else
              more_to_carve = false

        path

    module.exports = Carver


Changed stuff (sorry - shitty week, shitty commit message)

    q     = require 'q'
    fs    = require 'fs'
    async = require 'async'
    uuid  = require 'uuid-1345'

    microtime = require 'microtime'

    EventEmitter2 = require('eventemitter2').EventEmitter2
    emitter       = new EventEmitter2 wildcard: true

    available   = { }
    initialized = { }
    listeners   = { }

    underscore = (value) -> value.replace /\W+/g, '_'
    strip      = (name)  -> name.toLowerCase().replace /\W+/g, ''

    SEARCH_ROOTS  = { }
    WRAPPED       = { }
    _INITIALIZERS = { }
    HYDRATED      = { }
    LOCALS        = { }

    SKIP_WRAP = [
      'constructor'
      'initialize'
      'details'
      'fire'
      'warn'
      'listen'
      'set'
      'runtime_exception'
    ]

    HYRDATION_LISTENERS = { }

    INITIALIZERS = (cls, method) ->
      _INITIALIZERS[cls] = [ ] unless _INITIALIZERS[cls]?

      _INITIALIZERS[cls].push fn: method, called: false

    WRAP = (cls, method) ->
      WRAPPED[cls] = [ ] unless WRAPPED[cls]?

      WRAPPED[cls].indexOf(method) < 0

    determine_local = (token) ->
      project_local = false
      node_local    = false
      name          = token.substring 1, token.length

      if token[0] == '.'
        project_local = true
      else if token[0] == '~'
        node_local = true
      else
        name = token

      [project_local, node_local, name]

    parse_name_and_local = (tokens) ->
      [project_local, node_local, name] = determine_local tokens[0].trim()

      if tokens.length == 2
        alias = tokens[1].trim()
      else if tokens.length == 1
        alias = name
      else
        return Madul.FIRE '!.Madul.dependency_spec.alias.invalid', tokens[1]

      [project_local, node_local, name, alias]

    parse_alias_and_parent = (token) ->
      t = token.split '->'

      if t[0].indexOf('#') > -1
        r      = t[0].split '#'
        parent = r[0].trim()
        t[0]   = r[1]

      [parent, t]

    extract_properties = (token) ->
      [parent, t] = parse_alias_and_parent token
      [project_local, node_local, name, alias] = parse_name_and_local t

      [project_local, node_local, name, alias, parent]

    class Madul

      details: =>
        name  = @.__proto__.constructor.name
        props = for own key, val of @.__proto__
          if key != 'constructor' &&
             key != 'details'     &&
             key != 'initialize'  && ((
               key[0]            == '_' &&
               @.__proto__[key]? == false
             ) || key[0] != '_')

            if typeof val == 'function'
              value = val.constructor.name
            else if Array.isArray val
              value = for v in val
                if typeof v == 'function'
                  v.constructor.name
                else if typeof v == 'object'
                  v.constructor.name
                else
                  v
              value = value.join ','
            else if typeof val == 'object'
              value = val.constructor.name
            else
              value = val

            "#{key} => #{value}"

        "\n  #{name}\n    #{props.filter((e) -> e?).join '\n    '}"

      @LISTEN: (event, callback) => emitter.on event, callback

      @FIRE: (event, args) => emitter.emit event, args

      @SEARCH_ROOT: (mod, path) =>
        Madul.FIRE '$.Madul.search_root.register', "#{mod}": path

        SEARCH_ROOTS[mod] = path.substring 0, path.lastIndexOf '/'

      @PARSE_SPEC: (value) =>
        tokens = value.split '='

        [project_local, node_local, name, alias, parent] = extract_properties tokens[0]

        if tokens.length == 2
          initializer = tokens[1].trim()
          toks        = initializer.split ':'
          initializer = toks[0].trim()

          if toks.length == 2
            prerequisites = toks[1].split(',').map (p) => p.trim()
          else if toks.length > 2
            return Madul.FIRE '!.Madul.dependency_spec.initializer.invalid', tokens[1]

        else if tokens.length > 2
          return Madul.FIRE '!.Madul.dependency_spec.invalid', d

        { ref: alias || name, alias, name, parent, initializer, prerequisites, project_local, node_local }

      @BUILD_SPEC: ({ search_root, name, alias, initializer, prerequisites }) =>
        unless name?
          return Madul.FIRE '!.Madul.dependency_spec.name-required', arguments[0]

        if prerequisites? && initializer? == false
          return Madul.FIRE '!.Madul.dependency_spec.prerequisites-require-initializer', arguments[0]

        "#{if search_root?   then "#{search_root}#"            else ''}\
         #{name}\
         #{if alias?         then " -> #{alias}"               else ''}\
         #{if initializer?   then " = #{initializer}"          else ''}\
         #{if prerequisites? then ":#{prerequisites.join ','}" else ''}\
        "

      listen: (event, callback) ->
        Madul.LISTEN "*.#{@constructor.name}.#{event}", callback

      fire: (event, args) ->
        Madul.FIRE "@.#{@constructor.name}.#{event}", args

      warn: (event, details, finisher) =>
        Madul.FIRE "!.#{@constructor.name}.#{event}", details

        finisher() if typeof finisher == 'function'

      runtime_exception: (exit = false) =>
        (err) =>
          Madul.FIRE "!.#{@constructor.name}.runtime-exception",
            stack:     err.stack
            message:   err.message
            timestamp: microtime.now()

          process.exit(1) if exit == 'DIE_ON_EXCEPTION'

      set: (prop, val) =>
        proto = @.__proto__
        mod   = available[strip proto.constructor.name.toLowerCase()]

        if mod?
          mod.__proto__[prop] = val

      _report: (method, state, id, args, breaker) =>
        invoke = undefined
        params =
          uuid:      id
          args:      args
          timestamp: microtime.now()

        if state == 'reject'
          if typeof breaker == 'function'
            stop = new Error()
            stop.break = true

            breaker stop

          @warn "#{method}.#{state}", params
        else
          @fire "#{method}.#{state}", params

      _respond: (proto, method, id, deferred) =>
        (state, data, breaker) =>
          process.nextTick =>
            deferred[state] data

            proto._report method, state, id, data, breaker

      _prep_invocation: (proto, method, input, def) =>
        id  = uuid.v4fast()
        res = proto._respond proto, method, id, def
        e   = (brk) =>
          error = (err) => res 'reject', err, brk

          if typeof brk == 'function'
            error
          else
            error brk

        if Array.isArray input
          input.push (out) => res 'resolve', out
          input.push e
          input.push (out) => res 'notify',  out
        else if typeof input == 'object'
          input.done   = (out) => res 'resolve', out
          input.fail   = e
          input.update = (out) => res 'notify',  out

          input = [ input ]
        else
          msg = "input to #{proto.constructor.name}.#{method} must be an Array of Object"

          proto.warn.call proto, 'input.invalid', msg

          throw new Error msg

        proto._report method, 'invoke', id, input

        input

      _process_decorators: (proto, type) =>
        if Array.isArray proto[type]
          proto[type].map (b) => Madul.PARSE_SPEC(b).ref
        else if typeof proto[type] == 'string'
          [ Madul.PARSE_SPEC(proto[type]).ref ]
        else
          [ ]

      _do_wrap: (proto, method, wrapped) =>
        if SKIP_WRAP.includes method
          wrapped()
        else
          Madul.FIRE "$.#{proto.constructor.name}.#{method}.wrap"

          if typeof proto[method] == 'function'
            proto["_#{method}"] = proto[method]

            proto[method] = =>
              def   = q.defer()
              args  = Array.prototype.slice.call arguments
              input = proto._prep_invocation proto, method, args, def

              try
                proto["_#{method}"].apply proto, input
              catch e
                failed = if input[0].fail? then input[0].fail else input[input.length - 2]

                proto.warn.call proto, 'runtime-exception', stack: e.stack, message: e.message

                failed e.message

              def.promise

            INITIALIZERS proto.constructor.name, method if method[0] == '$'

            wrapped()
          else if typeof proto[method] == 'object'

            if Array.isArray proto[method].validate
              args_format = 'ARRAY'
              decs        = proto[method].validate

              proto[method].validators = for val, i in decs
                "#{i}": Madul.PARSE_SPEC(val).ref
            else if typeof proto[method].validate == 'object'
              args_format = 'OBJECT'
              decs        = [ ]

              for own key, val of proto[method].validate
                if Array.isArray val
                  for v in val
                    decs.push v
                else if typeof val == 'object'
                  for own k, v of val
                    if Array.isArray v
                      for V in v
                        decs.push V
                    else
                      decs.push v
                else
                  decs.push val

              proto[method].validators = [ ]

              for own key, val of proto[method].validate
                if Array.isArray val
                  proto[method].validators.push "#{key}": for v in val
                    Madul.PARSE_SPEC(v).ref
                else if typeof val == 'object'
                  for own k, v of val
                    if Array.isArray v
                      proto[method].validators.push "#{key}.#{k}": for V in v
                        Madul.PARSE_SPEC(V).ref
                    else
                      proto[method].validators.push "#{key}.#{k}": Madul.PARSE_SPEC(v).ref
                else
                  proto[method].validators.push "#{key}": Madul.PARSE_SPEC(val).ref
            else if typeof proto[method].validate == 'string'
              args_format = 'ARRAY'
              decs        = [ proto[method].validate ]

              proto[method].validators = [ { 0: Madul.PARSE_SPEC(proto[method].validate).ref }]
            else if typeof proto[method].validate != 'undefined'
              proto.warn.call proto, 'meta.invalid', validate: 'must be a String, Object, or Array'
              return
            else
              args_format = 'ARRAY'
              decs        = [ ]

            if Array.isArray proto[method].before
              decs = decs.concat proto[method].before
            else if typeof proto[method].before == 'string'
              decs = decs.concat [ proto[method].before ]

            if Array.isArray proto[method].after
              decs = decs.concat proto[method].after
            else if typeof proto[method].after == 'string'
              decs = decs.concat [ Madul.PARSE_SPEC proto[method].after ]

            deduped = decs.filter (e, i, a) => a.indexOf(e) == i

            Madul.FIRE '$.Madul.decorators.hydrate', deduped

            handle_err = (fn, args, next, reject) =>
              (err) =>
                proto.warn.call proto, "#{fn}.validator.failed", args
                reject err

                stop = new Error()
                stop.break = true

                next stop

            @_do_hydrate proto, deduped, =>
              proto["_#{method}"] = proto[method]

              proto[method] = =>
                def = q.defer()
                me  = proto["_#{method}"]

                if args_format == 'ARRAY'
                  args = Array.prototype.slice.call arguments
                else if args_format == 'OBJECT'
                  args = arguments[0]
                else
                  proto.warn.call proto, 'cannot-continue', 'No arg format specified'

                  return def.reject 'No arg format specified'

                before = @_process_decorators me, 'before'
                after  = @_process_decorators me, 'after'

                q.Promise (resolve, reject) =>
                  async.eachSeries me.validators, (v, next) =>
                    arg       = Object.keys(v)[0]
                    validator = v[arg]
                    input     = arg
                    prop      = args

                    for a in arg.split '.'
                      prop  = prop[a]
                      input = a

                    if Array.isArray validator
                      async.eachSeries validator, (v, nxt) =>
                        proto.fire.call proto, 'validator.execute', "#{v}": prop

                        proto[v].EXECUTE.call proto[v], input, prop
                          .then nxt
                          .catch handle_err v, { "#{input}": prop }, nxt, reject
                      , next
                    else
                      proto.fire.call proto, 'validator.execute', "#{validator}": prop

                      proto[validator].EXECUTE.call proto[validator], input, prop
                        .then next
                        .catch handle_err v, { "#{input}": prop }, next, reject
                  , resolve
                .then =>
                  q.Promise (resolve, reject) =>
                    async.eachSeries before, (b, next) =>
                      proto.fire.call proto, 'before_filter.execute', "#{b}": args

                      proto[b].before.call proto[b], args
                        .then next
                        .catch handle_err b, args, next, reject
                    , resolve
                .then =>
                  input = proto._prep_invocation proto, method, args, def

                  try
                    me.behavior.apply me, input
                  catch e
                    failed = if input[0].fail? then input[0].fail else input[input.length - 2]

                    me.warn.call me, 'runtime-exception', stack: e.stack, message: e.message

                    failed e.message
                .then (result) =>
                  q.Promise (resolve, reject) =>
                    if after.length > 0
                      async.eachSeries after, (a, next) =>
                        proto.fire.call proto, 'after_filter.execute', "#{a}": args

                        proto[a].after.call proto[a], result
                          .then next
                          .catch handle_err b, args, next, reject
                      , => resolve result
                    else
                      process.nextTick => resolve result
                .then  (out) => def.resolve out
                .catch (err) => def.reject  err

                def.promise

              INITIALIZERS proto.constructor.name, method if method[0] == '$'

              wrapped()
          else
            proto.warn.call proto, 'cannot-wrap', method

          WRAPPED[proto.constructor.name].push method

      _wrap_methods: (proto, callback) =>
        name    = proto.constructor.name
        to_wrap = [ ]

        while proto? && Object.keys(proto).length > 0 && WRAPPED[name]? == false
          to_wrap.push { proto, name, methods: [ ] }

          for own prop, body of proto
            if prop[0]                  != '_'         &&
               typeof proto["_#{prop}"] == 'undefined' &&
               WRAP(name, prop)         == true

              continue if proto.deps? && proto.deps.indexOf(prop.replace(/_/g, '-')) > -1

              if typeof body == 'function' || typeof body?.behavior == 'function'
                to_wrap[to_wrap.length - 1].methods.push prop

          proto = proto.__proto__
          name  = proto.constructor.name

        async.each to_wrap.reverse(), (wrap, completed) =>
          async.each wrap.methods, (method, next) =>
            wrap.proto._do_wrap wrap.proto, method, next
          , => @_call_initializers_for wrap.proto, wrap.name, completed
        , callback

      _make_available: (name, mod) =>
        Madul.FIRE "$.#{name}.available"

        available[strip name] = mod

      _hydrate_deps: (proto, done) =>
        protos = [ proto ]

        if proto.__proto__?
          while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            protos.push proto = proto.__proto__

        async.each protos.reverse(), (p, next) =>
          name = p.constructor.name

          if p.hasOwnProperty 'deps'
            if HYDRATED[name] == undefined
              HYDRATED[name]            = false
              HYRDATION_LISTENERS[name] = [ next ]

              p._do_hydrate p, p.deps, =>
                HYDRATED[name] = true

                for listener in HYRDATION_LISTENERS[name]
                  listener()
            else if HYDRATED[name] == false
              HYRDATION_LISTENERS[name].push next
            else if HYDRATED[name] == true
              next()
          else
            next()
        , done

      _init_if_madul: (path, callback) =>
        mod      = require path
        proto    = mod
        is_madul = false

        while proto
          is_madul = true if proto.name == 'Madul'
          if is_madul                            == false      &&
             proto.constructor.name              != 'Object'   &&
             typeof proto                        == 'function' &&
             typeof proto.__super__?.constructor == 'function'
            proto = proto.__super__.constructor
          else
            proto = undefined

        if is_madul
          new mod()
            .initialize()
            .then callback
        else
          callback mod

      _load: (me, name, path, loaded) =>
        me._init_if_madul path, (mod) =>
          me._make_available name, mod
          loaded mod

      _check: (me, path, dep, ref, finished) =>
        error = true

        fs.readdir path, (err, files) =>
          if err?
            me.warn 'file-not-found', err

            finished 'NOT_FOUND'
          else
            async.each files, (f, next) =>
              depth = "#{path}/#{f}"

              fs.stat depth, (e, s) =>
                if s.isDirectory() && f[0] != '.'
                  me._check me, depth, dep, ref, next
                else if s.isFile() && f.substring(0, f.length - 3) == dep
                  me._load me, ref, depth, (mod) =>
                    me._do_add me, ref, next
                    error = undefined
                else
                  next()
            , => finished error

      _do_add: (me, ref, next) =>
        Madul.FIRE "$.#{me.constructor.name}.dependency.register", ref

        me[underscore ref] = available[strip ref]

        next()

      _add: (me, ref, path, next) =>
        me._init_if_madul path, (mod) =>
          me._make_available ref, mod
          me._do_add me, ref, next

      _do_hydrate: (proto, deps, hydration_complete) =>
        initers = [ ]

        async.each deps, (d, next) =>
          spec = Madul.PARSE_SPEC d

          if spec.prerequisites?
            insert_at = for p in spec.prerequisites
              index_for = (sibling) => sibling.alias == p.alias

              initers.find index_for

            insert_at.sort()

            insert_at = if insert_at.length == 0 then 0 else insert_at[insert_at.length - 1]

            initers.splice insert_at, 0, alias: spec.alias, execute: spec.initializer
          else if spec.initializer?
            initers.push alias: spec.alias, execute: spec.initializer

          if typeof proto[underscore spec.ref] == 'undefined'
            if available[strip spec.ref]? == true
              proto._do_add proto, spec.ref, next
            else
              if spec.parent?
                Madul.FIRE '$.Madul.search_root.load', { name: spec.name, alias: spec.alias, parent: spec.parent }

                @_do_hydrate proto, [ spec.parent ], =>
                  if SEARCH_ROOTS[spec.parent] == undefined
                    SEARCH_ROOTS[spec.parent] = @_find_code_root spec.parent

                  proto._check proto, SEARCH_ROOTS[spec.parent], spec.name, spec.ref, next
              else if spec.node_local
                key = @_find_require_key proto.constructor.name

                async.eachSeries require.cache[key].paths, (path, nxt) =>
                  @_load_from_package_json proto, path, spec.name, spec.ref, =>
                    stop = new Error()
                    stop.break = true

                    next()
                    nxt stop
                  , nxt
              else if spec.project_local
                proto._check proto, LOCALS[proto.constructor.name], spec.name, spec.ref, next
              else
                try
                  proto._add proto, spec.ref, spec.name, next
                catch e
                  path = "#{process.cwd()}/node_modules"

                  @_load_from_package_json proto, path, spec.name, spec.ref, next, =>
                    pth = LOCALS[proto.constructor.name]

                    try
                      proto._check proto, pth, spec.name, spec.ref, (err) =>
                        if err?
                          proto._check proto, "#{process.cwd()}/dist", spec.name, spec.ref, next
                        else
                          next()

          else
            next()
        , =>
          async.eachSeries initers, (initer, next) =>
            proto[initer.execute].apply(@).then next
          , hydration_complete

      _load_from_package_json: (proto, path, name, ref, success, fail) =>
        pkg = "#{path}/#{name}/package.json"

        fs.stat pkg, (err, stat) =>
          if stat?.isFile()
            fs.readFile pkg, 'utf8', (err, data) =>
              json = JSON.parse data
              main = json.main || json._main || 'index.js'
              path = "#{path}/#{name}/#{main}"

              proto._add proto, ref, path, success
          else
            fail()

      _finish_up: (proto, args) =>
        name = proto.constructor.name

        if listeners[name]?
          async.each listeners[name], (listener, next) =>
            listener @
            next()
          , => @_register_initialized name
        else
          @_register_initialized name

      _register_initialized: (name) =>
        @_make_available name, @

        initialized[name] = true

        Madul.FIRE "$.#{name}.initialized"

      _call_initializers_for: (proto, name, callback) =>
        if _INITIALIZERS[name]?
          async.each _INITIALIZERS[name], (initializer, next) =>
            if initializer.called == false
              proto[initializer.fn].apply proto
                .then =>
                  initializer.called = true
                  next()
            else
              next()
          , callback
        else
          callback()

      _do_find: (thing, cb) =>
        file  = thing.toLowerCase()
        found = for own key, val of require.cache
          if key.replace(/_/g, '').toLowerCase().includes file
            cb key

        found
          .filter (f) => f?
          .sort((a, b) => a.length - b.length)[0]

      _find_require_key: (resource) =>
        @_do_find resource, (key) => key

      _find_code_root: (resource) =>
        @_do_find resource, (key) => key.substring(0, key.lastIndexOf '/').split '/'
          .join '/'

      initialize: =>
        deferred = q.defer()
        proto    = @.__proto__
        name     = proto.constructor.name

        Madul.FIRE "$.#{name}.request_instance"

        if initialized[name] == undefined
          Madul.FIRE "$.#{name}.initialize"

          initialized[name] = false
          listeners[name]   = [ deferred.resolve ]

          if LOCALS[name] == undefined
            LOCALS[name] = @_find_code_root name

          @_hydrate_deps proto, => @_wrap_methods proto, => @_finish_up proto
        else if initialized[name] == false
          listeners[name].push deferred.resolve
        else if initialized[name] == true
          process.nextTick => deferred.resolve available[strip name]

        deferred.promise

    module.exports = Madul

    q     = require 'q'
    fs    = require 'fs'
    async = require 'async'
    uuid  = require 'uuid-1345'

    microtime = require 'microtime'

    EventEmitter2 = require('eventemitter2').EventEmitter2
    emitter       = new EventEmitter2 wildcard: true

    available   = { }
    initialized = { }
    listeners   = { }

    underscore = (value) -> value.replace /\W+/g, '_'
    strip      = (name)  -> name.toLowerCase().replace /\W+/g, ''

    SEARCH_ROOTS  = { }
    WRAPPED       = { }
    _INITIALIZERS = { }
    HYDRATED      = { }
    LOCALS        = { }

    HYRDATION_LISTENERS = { }

    INITIALIZERS = (cls, method) ->
      _INITIALIZERS[cls] = [ ] unless _INITIALIZERS[cls]?

      _INITIALIZERS[cls].push fn: method, called: false

    WRAP = (cls, method) ->
      unless WRAPPED[cls]?
        WRAPPED[cls] = [
          'constructor'
          'initialize'
          'details'
        ]

      WRAPPED[cls].indexOf(method) < 0

    determine_local = (token) ->
      project_local = false
      node_local    = false
      name          = token.substring 1, token.length

      if token[0] == '.'
        project_local = true
      else if token[0] == '~'
        node_local = true
      else
        name = token

      [project_local, node_local, name]

    parse_name_and_local = (tokens) ->
      [project_local, node_local, name] = determine_local tokens[0].trim()

      if tokens.length == 2
        alias = tokens[1].trim()
      else if tokens.length == 1
        alias = name
      else
        return Madul.FIRE '!.Madul.dependency_spec.alias.invalid', tokens[1]

      [project_local, node_local, name, alias]

    parse_alias_and_parent = (token) ->
      t = token.split '->'

      if t[0].indexOf('#') > -1
        r      = t[0].split '#'
        parent = r[0].trim()
        t[0]   = r[1]

      [parent, t]

    extract_properties = (token) ->
      [parent, t] = parse_alias_and_parent token
      [project_local, node_local, name, alias] = parse_name_and_local t

      [project_local, node_local, name, alias, parent]

    class Madul

      details: =>
        name  = @.__proto__.constructor.name
        props = for own key, val of @.__proto__
          if key != 'constructor' &&
             key != 'details'     &&
             key != 'initialize'  && ((
               key[0]            == '_' &&
               @.__proto__[key]? == false
             ) || key[0] != '_')

            if typeof val == 'function'
              value = val.constructor.name
            else if Array.isArray val
              value = for v in val
                if typeof v == 'function'
                  v.constructor.name
                else if typeof v == 'object'
                  v.constructor.name
                else
                  v
              value = value.join ','
            else if typeof val == 'object'
              value = val.constructor.name
            else
              value = val

            "#{key} => #{value}"

        "\n  #{name}\n    #{props.filter((e) -> e?).join '\n    '}"

      @LISTEN: (event, callback) => emitter.on event, callback

      @FIRE: (event, args) => emitter.emit event, args

      @SEARCH_ROOT: (mod, path) =>
        Madul.FIRE '$.Madul.search_root.register', "#{mod}": path

        SEARCH_ROOTS[mod] = path.substring 0, path.lastIndexOf '/'

      @PARSE_SPEC: (value) =>
        tokens = value.split '='

        [project_local, node_local, name, alias, parent] = extract_properties tokens[0]

        if tokens.length == 2
          initializer = tokens[1].trim()
          toks        = initer.split ':'
          initializer = toks[0].trim()

          if toks.length == 2
            prerequisites = toks[1].split(',').map (p) => p.trim()
          else if toks.length > 2
            return Madul.FIRE '!.Madul.dependency_spec.initializer.invalid', tokens[1]

        else if tokens.length > 2
          return Madul.FIRE '!.Madul.dependency_spec.invalid', d

        { ref: alias || name, alias, name, parent, initializer, prerequisites, project_local, node_local }

      @BUILD_SPEC: ({ search_root, name, alias, initializer, prerequisites }) =>
        unless name?
          return Madul.FIRE '!.Madul.dependency_spec.name-required', arguments[0]

        if prerequisites? && initializer? == false
          return Madul.FIRE '!.Madul.dependency_spec.prerequisites-require-initializer', arguments[0]

        "#{if search_root?   then "#{search_root}#"            else ''}\
         #{name}\
         #{if alias?         then " -> #{alias}"               else ''}\
         #{if initializer?   then " = #{initializer}"          else ''}\
         #{if prerequisites? then ":#{prerequisites.join ','}" else ''}\
        "

      listen: (event, callback) ->
        Madul.LISTEN "*.#{@constructor.name}.#{event}", callback

      fire: (event, args) ->
        Madul.FIRE "@.#{@constructor.name}.#{event}", args

      warn: (event, details) ->
        Madul.FIRE "!.#{@constructor.name}.#{event}", details

      _report: (name, method, state, id, args) =>
        if state == 'reject'
          Madul.FIRE "!.#{name}.#{method}.#{state}",
            uuid:      id
            message:   args
            timestamp: microtime.now()
        else
          Madul.FIRE "@.#{name}.#{method}.#{state}",
            uuid:      id
            args:      args
            timestamp: microtime.now()

      _respond: (proto, method, id, deferred) =>
        (state, data) =>
          process.nextTick =>
            deferred[state] data

            proto._report proto.constructor.name, method, state, id, data

      _prep_invocation: (proto, method, input, def) =>
        id  = uuid.v4fast()
        res = proto._respond proto, method, id, def

        if Array.isArray input
          input.push (out) => res 'resolve', out
          input.push (err) => res 'reject',  err
          input.push (out) => res 'notify',  out
        else if typeof input == 'object'
          input.done   = (out) => res 'resolve', out
          input.fail   = (err) => res 'reject',  err
          input.update = (out) => res 'notify',  out

          input = [ input ]
        else
          msg = "input to #{proto.constructor.name}.#{method} must be an Array of Object"
          proto.warn.call proto, 'input.invalid', msg
          throw new Error msg

        proto._report proto.constructor.name, method, 'invoke', id, input

        input

      _process_decorators: (proto, type) =>
        if Array.isArray proto[type]
          proto[type].map (b) => Madul.PARSE_SPEC(b).ref
        else if typeof proto[type] == 'string'
          [ Madul.PARSE_SPEC(proto[type]).ref ]
        else
          [ ]

      _do_wrap: (proto, method) =>
        if method != 'fire' && method != 'warn' && method != 'listen'
          Madul.FIRE "$.#{proto.constructor.name}.#{method}.wrap"

          if typeof proto[method] == 'function'
            proto["_#{method}"] = proto[method]

            proto[method] = =>
              def  = q.defer()
              args = Array.prototype.slice.call arguments

              proto["_#{method}"].apply proto, proto._prep_invocation proto, method, args, def

              def.promise
          else if typeof proto[method] == 'object'

            if Array.isArray proto[method].validate
              args_format = 'ARRAY'
              decs        = proto[method].validate

              proto[method].validators = for val, i in decs
                "#{i}": Madul.PARSE_SPEC(val).ref
            else if typeof proto[method].validate == 'object'
              args_format = 'OBJECT'
              decs        = [ ]

              for own key, val of proto[method].validate
                if Array.isArray val
                  for v in val
                    decs.push v
                else
                  decs.push val

              proto[method].validators = for own key, val of proto[method].validate
                if Array.isArray val
                  "#{key}": for v in val
                    Madul.PARSE_SPEC(v).ref
                else
                  "#{key}": Madul.PARSE_SPEC(val).ref
            else if typeof proto[method].validate == 'string'
              args_format = 'ARRAY'
              decs        = [ proto[method].validate ]

              proto[method].validators = [ { 0: Madul.PARSE_SPEC(proto[method].validate).ref }]
            else if typeof proto[method].validate != 'undefined'
              proto.warn.call proto, 'meta.invalid', validate: 'must be a String, Object, or Array'
              return
            else
              args_format = 'ARRAY'
              decs        = [ ]

            if Array.isArray proto[method].before
              decs = decs.concat proto[method].before
            else if typeof proto[method].before == 'string'
              decs = decs.concat [ proto[method].before ]

            if Array.isArray proto[method].after
              decs = decs.concat proto[method].after
            else if typeof proto[method].after == 'string'
              decs = decs.concat [ Madul.PARSE_SPEC proto[method].after ]

            Madul.FIRE '$.Madul.decorators.hydrate', decs

            @_do_hydrate proto, decs, =>
              proto["_#{method}"] = proto[method]

              proto[method] = =>
                def = q.defer()
                me  = proto["_#{method}"]

                if args_format == 'ARRAY'
                  args = Array.prototype.slice.call arguments
                else if args_format == 'OBJECT'
                  args = arguments[0]
                else
                  proto.warn.call proto, 'cannot-continue', 'No arg format specified'

                  return def.reject 'No arg format specified'

                validators = [ ]

                for v in me.validators
                  arg       = Object.keys(v)[0]
                  validator = v[arg]

                  if Array.isArray validator
                    for v in validator
                      Madul.FIRE '$.Madul.validator.execute', "#{v}": args[arg]

                      validators.push proto[v].EXECUTE.call proto[v], arg, args[arg]
                  else
                    Madul.FIRE '$.Madul.validator.execute', "#{validator}": args[arg]

                    validators.push proto[validator].EXECUTE.call proto[validator], arg, args[arg]

                before = @_process_decorators me, 'before'
                after  = @_process_decorators me, 'after'

                q.all validators
                .then =>
                  q.all before.map (b) =>
                    Madul.FIRE '$.Madul.before_filter.execute', "#{b}": args
                    proto[b].before.call proto[b], args
                .then =>
                  me.behavior.apply me, @_prep_invocation proto, method, args, def
                .then (result) =>
                  if after.length > 0
                    q.all after.map (a) =>
                      Madul.FIRE '$.Madul.after_filter.execute', "#{a}": result
                      proto[a].after.call proto[a], result
                  else
                    d = q.defer()

                    process.nextTick => d.resolve result

                    d.promise
                .then  (out) => def.resolve out
                .catch (err) => def.reject  err

                def.promise
          else
            proto.warn.call proto, 'cannot-wrap', method

          WRAPPED[proto.constructor.name].push method

      _wrap_methods: (proto, callback) =>
        name    = proto.constructor.name
        to_wrap = [ ]

        while proto? && Object.keys(proto).length > 0 && WRAPPED[name]? == false
          to_wrap.push { proto, name, methods: [ ] }

          for own prop, body of proto
            if prop[0]                  != '_'         &&
               typeof proto["_#{prop}"] == 'undefined' &&
               WRAP(name, prop)         == true

              continue if proto.deps? && proto.deps.indexOf(prop.replace(/_/g, '-')) > -1

              if typeof body == 'function' || typeof body?.behavior == 'function'
                to_wrap[to_wrap.length - 1].methods.push prop

          proto = proto.__proto__
          name  = proto.constructor.name

        async.each to_wrap.reverse(), (wrap, completed) =>
          async.each wrap.methods, (method, next) =>
            wrap.proto._do_wrap wrap.proto, method

            INITIALIZERS wrap.name, method if method[0] == '$'

            next()
          , => @_call_initializers_for wrap.proto, wrap.name, completed
        , callback

      _make_available: (name, mod) =>
        Madul.FIRE "$.#{name}.available"

        available[strip name] = mod

      _hydrate_deps: (proto, done) =>
        protos = [ proto ]

        if proto.__proto__?
          while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            protos.push proto = proto.__proto__

        async.each protos.reverse(), (p, next) =>
          name = p.constructor.name

          if p.hasOwnProperty 'deps'
            if HYDRATED[name] == undefined
              HYDRATED[name]            = false
              HYRDATION_LISTENERS[name] = [ next ]

              p._do_hydrate p, p.deps, =>
                HYDRATED[name] = true

                for listener in HYRDATION_LISTENERS[name]
                  listener()
            else if HYDRATED[name] == false
              HYRDATION_LISTENERS[name].push next
            else if HYDRATED[name] == true
              next()
          else
            next()
        , done

      _init_if_madul: (path, callback) =>
        mod      = require path
        proto    = mod
        is_madul = false

        while proto
          is_madul = true if proto.name == 'Madul'
          if is_madul                            == false      &&
             proto.constructor.name              != 'Object'   &&
             typeof proto                        == 'function' &&
             typeof proto.__super__?.constructor == 'function'
            proto = proto.__super__.constructor
          else
            proto = undefined

        if is_madul
          new mod()
            .initialize()
            .then callback
        else
          callback mod

      _load: (me, name, path, loaded) =>
        me._init_if_madul path, (mod) =>
          me._make_available name, mod
          loaded mod

      _check: (me, path, dep, ref, finished) =>
        fs.readdir path, (err, files) =>
          if err?
            me.warn 'file-not-found', err

            done type: 'ERROR', info: err
          else
            async.each files, (f, next) =>
              depth = "#{path}/#{f}"

              fs.stat depth, (e, s) =>
                if s.isDirectory() && f[0] != '.'
                  me._check me, depth, dep, ref, next
                else if s.isFile() && f.substring(0, f.length - 3) == dep
                  me._load me, ref, depth, (mod) => me._do_add me, mod, ref, next
                else
                  next()
            , finished

      _do_add: (me, mod, ref, next) =>
        Madul.FIRE "$.#{me.constructor.name}.dependency.register", ref

        me[underscore ref] = mod

        next()

      _add: (me, ref, path, next) =>
        me._init_if_madul path, (mod) =>
          me._make_available ref, mod
          me._do_add me, mod, ref, next

      _do_hydrate: (proto, deps, hydration_complete) =>
        initers = [ ]

        async.each deps, (d, next) =>
          spec = Madul.PARSE_SPEC d

          if spec.prerequisites?
            insert_at = for p in spec.prerequisites
              index_for = (sibling) => sibling.alias == p.alias

              initers.find index_for

            insert_at.sort()

            insert_at = if insert_at.length == 0 then 0 else insert_at[insert_at.length - 1]

            initers.splice insert_at, 0, alias: spec.alias, execute: spec.initializer

          if typeof proto[underscore spec.ref] == 'undefined'
            if available[strip spec.ref]? == true
              proto._do_add proto, available[strip spec.ref], spec.ref, next
            else
              if spec.parent?
                Madul.FIRE '$.Madul.search_root.load', { name: spec.name, alias: spec.alias, parent: spec.parent }

                @_do_hydrate proto, [ spec.parent ], =>
                  if SEARCH_ROOTS[spec.parent] == undefined
                    SEARCH_ROOTS[spec.parent] = @_find_code_root spec.parent

                  proto._check proto, SEARCH_ROOTS[spec.parent], spec.name, spec.ref, next
              else if spec.node_local
                key = @_find_require_key proto.constructor.name

                async.eachSeries require.cache[key].paths, (path, nxt) =>
                  @_load_from_package_json proto, path, spec.name, spec.ref, =>
                    stop = new Error()
                    stop.break = true

                    next()
                    nxt stop
                  , nxt
              else if spec.project_local
                proto._check proto, LOCALS[proto.constructor.name], spec.name, spec.ref, next
              else
                try
                  proto._add proto, spec.ref, spec.name, next
                catch e
                  path = "#{process.cwd()}/node_modules"

                  @_load_from_package_json proto, path, spec.name, spec.ref, next, =>
                    pth = LOCALS[proto.constructor.name]

                    proto._check proto, pth, spec.name, spec.ref, next
          else
            next()
        , =>
          async.eachSeries initers, (initer, next) =>
            proto[initer.execute].apply(@).then next
          , hydration_complete

      _load_from_package_json: (proto, path, name, ref, success, fail) =>
        pkg = "#{path}/#{name}/package.json"

        fs.stat pkg, (err, stat) =>
          if stat?.isFile()
            fs.readFile pkg, 'utf8', (err, data) =>
              json = JSON.parse data
              main = json.main || json._main || 'index.js'
              path = "#{path}/#{name}/#{main}"

              proto._add proto, ref, path, success
          else
            fail()

      _finish_up: (proto, args) =>
        name = proto.constructor.name

        if listeners[name]?
          async.each listeners[name], (listener, next) =>
            listener @
            next()
          , => @_register_initialized name
        else
          @_register_initialized name

      _register_initialized: (name) =>
        @_make_available name, @

        initialized[name] = true

        Madul.FIRE "$.#{name}.initialized"

      _call_initializers_for: (proto, name, callback) =>
        if _INITIALIZERS[name]?
          async.each _INITIALIZERS[name], (initializer, next) =>
            if initializer.called == false
              proto[initializer.fn].apply proto
                .then =>
                  initializer.called = true
                  next()
            else
              next()
          , callback
        else
          callback()

      _do_find: (thing, cb) =>
        file  = thing.toLowerCase()
        found = for own key, val of require.cache
          if key.replace(/_/g, '').toLowerCase().includes file
            cb key

        found
          .filter (f) => f?
          .sort((a, b) => a.length - b.length)[0]

      _find_require_key: (resource) =>
        @_do_find resource, (key) => key

      _find_code_root: (resource) =>
        @_do_find resource, (key) => key.substring(0, key.lastIndexOf '/').split '/'
          .join '/'

      initialize: =>
        deferred = q.defer()
        proto    = @.__proto__
        name     = proto.constructor.name

        Madul.FIRE "$.#{name}.request_instance"

        if initialized[name] == undefined
          Madul.FIRE "$.#{name}.initialize"

          initialized[name] = false
          listeners[name]   = [ deferred.resolve ]

          if LOCALS[name] == undefined
            LOCALS[name] = @_find_code_root name

          @_hydrate_deps proto, => @_wrap_methods proto, => @_finish_up proto
        else if initialized[name] == false
          listeners[name].push deferred.resolve
        else if initialized[name] == true
          process.nextTick => deferred.resolve available[strip name]

        deferred.promise

    module.exports = Madul


Disabled old test

Disabled old test


Switch ip address check order

Switch ip address check order

We don't want to always be gettign the ip address nginx is using ;-)

    Madul = require 'madul'

    STATUSES =
      COMPLETE: 'COMPLETE'
      ERROR:    'ERROR'
      PROGRESS: 'PROGRESS'

    EXCLUDE = [
      'MODULE'
      'ACTION'
      'EXECUTE'
    ]

    heartbeat = -> @isAlive = true

    class Server extends Madul

      deps: [ '~ws', '.db' ]

      boot: (args, done) ->
        wss = new @ws.Server port: args.ws.port

        setInterval ->
          wss.clients.forEach (ws) ->
            return ws.terminate() if ws.isAlive == false

            ws.isAlive = false
            ws.ping '', false, true
        , 10000

        wss.on 'error', (details) =>
          console.log 'WebSocket Server got error', details

        wss.on 'connection', (ws, req) =>
          try
            raw_ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress).split ':'
            ip_v4  = raw_ip.pop()
            ip_v6  = raw_ip.join ':'

            ip_v4 = null if ip_v4.split('.').length != 4
            ip_v6 = null if ip_v6.split(':').length  < 3
          catch e
            ip_v4  = null
            ip_v6  = null

          ws.isAlive = true
          ws.on 'pong', heartbeat

          ws.on 'message', (mess) =>
            try
              input = JSON.parse mess
            catch e
              return @_notify ws, STATUSES.ERROR, "Could not parse as JSON: #{mess}"

            args = { }

            for key, val of input
              if key != 'MODULE' and key != 'ACTION'
                args[key] = val

            @db.log_api_call
              v4_address: ip_v4
              v6_address: ip_v6
              module:     input.MODULE || 'NONE'
              action:     input.ACTION || 'NONE'
              args:       args

            input._client_ip_address =
              v4: ip_v4
              v6: ip_v6

            @handle input
              .then (output) => @_notify ws, STATUSES.COMPLETE, output
              .catch   (err) => @_notify ws, STATUSES.ERROR,    err.args? || err
              .progress (up) => @_notify ws, STATUSES.PROGRESS, up

          ws.on 'error', (details) =>
            console.log 'WebSocket got error', details

        done()

      _notify: (socket, status, data) =>
        if status == STATUSES.ERROR
          out = { status, message: data }
        else
          out = { status, data }

        try
          if socket.readyState == @ws.OPEN
            socket.send JSON.stringify(out), (err) =>
              if err?
                console.log 'Get error sending message', err
          else
            consle.log 'SOCKET NOT OPEN'
        catch e
          console.log 'Message send threw an error', e

      handle:
        validate:
          # KEY:    [ '.is_present', '.hasnt_expired' ]
          MODULE: '.is_present'
          ACTION: '.is_present'
        before:   '.init_execution_context'
        behavior: (input, done, fail, update) ->
          args = { }

          for own key, val of input
            args[key] = val unless EXCLUDE.includes key

          input.EXECUTE args
            .then     done
            .catch    fail
            .progress update
            .done()

    module.exports = Server

    Madul = require 'madul'

    STATUSES =
      COMPLETE: 'COMPLETE'
      ERROR:    'ERROR'
      PROGRESS: 'PROGRESS'

    EXCLUDE = [
      'MODULE'
      'ACTION'
      'EXECUTE'
    ]

    heartbeat = -> @isAlive = true

    class Server extends Madul

      deps: [ '~ws', '.db' ]

      boot: (args, done) ->
        wss = new @ws.Server port: args.ws.port

        setInterval ->
          wss.clients.forEach (ws) ->
            return ws.terminate() if ws.isAlive == false

            ws.isAlive = false
            ws.ping '', false, true
        , 10000

        wss.on 'error', (details) =>
          console.log 'WebSocket Server got error', details

        wss.on 'connection', (ws, req) =>
          try
            raw_ip = (req.connection.remoteAddress || req.headers['x-forwarded-for']).split ':'
            ip_v4  = raw_ip.pop()
            ip_v6  = raw_ip.join ':'

            ip_v4 = null if ip_v4.split('.').length != 4
            ip_v6 = null if ip_v6.split(':').length  < 3
          catch e
            ip_v4  = null
            ip_v6  = null

          ws.isAlive = true
          ws.on 'pong', heartbeat

          ws.on 'message', (mess) =>
            try
              input = JSON.parse mess
            catch e
              return @_notify ws, STATUSES.ERROR, "Could not parse as JSON: #{mess}"

            args = { }

            for key, val of input
              if key != 'MODULE' and key != 'ACTION'
                args[key] = val

            @db.log_api_call
              v4_address: ip_v4
              v6_address: ip_v6
              module:     input.MODULE || 'NONE'
              action:     input.ACTION || 'NONE'
              args:       args

            input._client_ip_address =
              v4: ip_v4
              v6: ip_v6

            @handle input
              .then (output) => @_notify ws, STATUSES.COMPLETE, output
              .catch   (err) => @_notify ws, STATUSES.ERROR,    err.args? || err
              .progress (up) => @_notify ws, STATUSES.PROGRESS, up

          ws.on 'error', (details) =>
            console.log 'WebSocket got error', details

        done()

      _notify: (socket, status, data) =>
        if status == STATUSES.ERROR
          out = { status, message: data }
        else
          out = { status, data }

        try
          if socket.readyState == @ws.OPEN
            socket.send JSON.stringify(out), (err) =>
              if err?
                console.log 'Get error sending message', err
          else
            consle.log 'SOCKET NOT OPEN'
        catch e
          console.log 'Message send threw an error', e

      handle:
        validate:
          # KEY:    [ '.is_present', '.hasnt_expired' ]
          MODULE: '.is_present'
          ACTION: '.is_present'
        before:   '.init_execution_context'
        behavior: (input, done, fail, update) ->
          args = { }

          for own key, val of input
            args[key] = val unless EXCLUDE.includes key

          input.EXECUTE args
            .then     done
            .catch    fail
            .progress update
            .done()

    module.exports = Server


fixing

fixing

Pretty simple setup here.

    handlebars = require 'handlebars'
    aws = require 'aws-sdk'
    shortid = require 'shortid'

Setup the connection to s3

    aws.config.update {
        accessKeyId: process.env["AWS_ACCESS_KEY"],
        secretAccessKey: process.env["AWS_SECRET_KEY"]
    }
    aws.config.region = 'us-east-1'
    s3 = new aws.S3()

Set the name for your domain.

    module.exports = (url) ->
      DOMAIN = process.env["DOMAIN"] || "shortcake.com"
      BUCKET = process.env["BUCKET"] || DOMAIN
      GA_ID = process.env["GA_ID"]
      shortid.characters "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ()"

This is our basic HTML template that we'll use for doing the redirect. It's just
going to send the user to another page as soon as it's loaded. There's no server
since it's just a static file, but everything will be tracked by GA.

      source = """

          <title>{{url}}</title>

      """

      template = handlebars.compile(source)

Data should just be the user's Google Analytics Id and the URL that they want to
minify.

      data = { url: url, ga_id: GA_ID }
      html = template(data)

We're going to generate a shortlink for the URL. [`shortid`](https://github.com/dylang/shortid) does a pretty good job at balancing between being short, and also
being unique!

      _id = shortid.generate()

We could break up the files by the [first 3 letters in the id](http://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html).
This makes it much easier for S3 to distribute the bucket (and greatly improves performance).
...but we're not going to do this yet.

      params = {
          Bucket: BUCKET,
          Key: _id
          ACL: "public-read",
          Body: html
      }
      s3.putObject params, (err, data) ->
          if err
              console.log err

We'll keep a running record of all links we've shortened and throw it into a
basic HTML page just to make it easy to do lookups.

      source = """

          <title>Shortened Links</title>

Shortened Links

              {{#objs}}

{{ Key }}

              {{/objs}}

      """
      params = { Bucket: BUCKET }
      s3.listObjects params, (err, objs) ->
        if err
            console.log "[ERROR]: could not list objects: " + err
        objs = objs.Contents.filter (obj) ->
          ! /index.html$/.test obj.Key
        html = handlebars.compile(source)({ objects: objs })
        params = {
            Bucket: BUCKET,
            Key: "index.html",
            ACL: "public-read",
            Body: html
        }
        s3.putObject params, (err, data) ->
            if err
                console.log "[ERROR]: " + err
        console.error "Visit: https://s3.amazonaws.com/#{BUCKET}/index.html"

Pretty simple setup here.

    handlebars = require 'handlebars'
    aws = require 'aws-sdk'
    shortid = require 'shortid'

Setup the connection to s3

    aws.config.update {
        accessKeyId: process.env["AWS_ACCESS_KEY"],
        secretAccessKey: process.env["AWS_SECRET_KEY"]
    }
    aws.config.region = 'us-east-1'
    s3 = new aws.S3()

Set the name for your domain.

    module.exports = (url) ->
      DOMAIN = process.env["DOMAIN"] || "shortcake.com"
      BUCKET = process.env["BUCKET"] || DOMAIN
      GA_ID = process.env["GA_ID"]
      shortid.characters "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ()"

This is our basic HTML template that we'll use for doing the redirect. It's just
going to send the user to another page as soon as it's loaded. There's no server
since it's just a static file, but everything will be tracked by GA.

      source = """

          <title>{{url}}</title>

      """

      template = handlebars.compile(source)

Data should just be the user's Google Analytics Id and the URL that they want to
minify.

      data = { url: url, ga_id: GA_ID }
      html = template(data)

We're going to generate a shortlink for the URL. [`shortid`](https://github.com/dylang/shortid) does a pretty good job at balancing between being short, and also
being unique!

      _id = shortid.generate()

We could break up the files by the [first 3 letters in the id](http://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html).
This makes it much easier for S3 to distribute the bucket (and greatly improves performance).
...but we're not going to do this yet.

      params = {
          Bucket: BUCKET,
          Key: _id
          ACL: "public-read",
          Body: html
      }
      s3.putObject params, (err, data) ->
          if err
              console.log err

We'll keep a running record of all links we've shortened and throw it into a
basic HTML page just to make it easy to do lookups.

      source = """

          <title>Shortened Links</title>

Shortened Links

              {{#objs}}

{{ Key }}

              {{/objs}}

      """
      params = { Bucket: BUCKET }
      s3.listObjects params, (err, objs) ->
        if err
            console.log "[ERROR]: could not list objects: " + err
        objs = objs.Contents.filter (obj) ->
          ! /index.html$/.test obj.Key
        html = handlebars.compile(source)({ objects: objs })
        params = {
            Bucket: BUCKET,
            Key: "index.html",
            ACL: "public-read",
            Body: html
        }
        s3.putObject params, (err, data) ->
            if err
                econsole.log "[ERROR]: " + err
        console.error "Visit: https://s3.amazonaws.com/#{BUCKET}/index.html"


more accurate seconds/year

more accurate seconds/year

    Template.registerHelper 'totalWaitTimeMinutes', ->
      minutes = (QueueMeta.findOne()?.totalWaitTimeSeconds / 60).toFixed 0
      "#{minutes} minutes"

    Template.registerHelper 'totalWaitTimeYears', ->
      secondsInYear = 31556900
      years = (QueueMeta.findOne()?.totalWaitTimeSeconds / secondsInYear ).toFixed 2
      "#{years} years"

    Template.registerHelper 'nameAllowedIn', ->
      QueueMeta.findOne()?.theOnlyConnectionAllowedIn?.name

    Template.registerHelper 'isBeingServed', ->
      queueEntry = Queue.findOne()
      queueMeta = QueueMeta.findOne()

Update timeCurrentTicketExpires hackily here...

      timeCurrentTicketExpires = Session.get 'timeCurrentTicketExpires'
      if timeCurrentTicketExpires isnt queueMeta?.timeCurrentTicketExpires
        Session.set 'timeCurrentTicketExpires', queueMeta?.timeCurrentTicketExpires

Not being served in either queueEntry or queueMeta are non-existant

      return no if not (queueEntry? and queueMeta?)
      now = moment().utc().toDate()
      isBeingServed = queueEntry._id is queueMeta?.currentlyServingQueueId and
        queueMeta.timeCurrentTicketExpires > now
      if isBeingServed
        cachedIsBeingServed = Session.get 'isBeingServed'
        Session.set 'isBeingServed', yes if cachedIsBeingServed is undefined or no
      return isBeingServed

    Template.registerHelper 'totalWaitTimeMinutes', ->
      minutes = (QueueMeta.findOne()?.totalWaitTimeSeconds / 60).toFixed 0
      "#{minutes} minutes"

    Template.registerHelper 'totalWaitTimeYears', ->
      secondsInYear = 31536000
      years = (QueueMeta.findOne()?.totalWaitTimeSeconds / secondsInYear ).toFixed 2
      "#{years} years"

    Template.registerHelper 'nameAllowedIn', ->
      QueueMeta.findOne()?.theOnlyConnectionAllowedIn?.name

    Template.registerHelper 'isBeingServed', ->
      queueEntry = Queue.findOne()
      queueMeta = QueueMeta.findOne()

Update timeCurrentTicketExpires hackily here...

      timeCurrentTicketExpires = Session.get 'timeCurrentTicketExpires'
      if timeCurrentTicketExpires isnt queueMeta?.timeCurrentTicketExpires
        Session.set 'timeCurrentTicketExpires', queueMeta?.timeCurrentTicketExpires

Not being served in either queueEntry or queueMeta are non-existant

      return no if not (queueEntry? and queueMeta?)
      now = moment().utc().toDate()
      isBeingServed = queueEntry._id is queueMeta?.currentlyServingQueueId and
        queueMeta.timeCurrentTicketExpires > now
      if isBeingServed
        cachedIsBeingServed = Session.get 'isBeingServed'
        Session.set 'isBeingServed', yes if cachedIsBeingServed is undefined or no
      return isBeingServed


Fix documentation list of generated versions

    Upload = require 's3-uploader'

Configure [s3-uploader](https://github.com/Turistforeningen/node-s3-uploader) to
store the following versions:

| Suffix    | Max Height | Max Width | Aspect Ratio |
|-----------|------------|-----------|--------------|
| `-full`   | `1200`     | `1200`    |              |
| `-800`    | `800`      | `800`     | `3:2`        |
| `-500`    | `500`      | `500`     | `3:2`        |
| `-260`    | `260`      | `260`     | `3:2`        |
| `-150`    | `150`      | `150`     | `3:2`        |
| `-thumb1` | `250`      | `250`     | `1:1`        |

    module.exports = new Upload process.env.AWS_BUCKET_NAME,
      aws:
        region: process.env.AWS_BUCKET_REGION
        path: process.env.AWS_BUCKET_PATH
        acl: 'public-read'
        httpOptions: timeout: 60000

      returnExif: true

      cleanup:
        original: true
        versions: true

      original:
        awsImageAcl: 'private'

      versions: [
        suffix: '-full'
        quality: 80
        maxHeight: 1200
        maxWidth: 1200
      ,
        suffix: '-800'
        maxHeight: 800
        maxWidth: 800
        aspect: '3:2'
      ,
        suffix: '-500'
        maxHeight: 500
        maxWidth: 500
        aspect: '3:2'
      ,
        suffix: '-260'
        maxHeight: 260
        maxWidth: 260
        aspect: '3:2'
      ,
        suffix: '-150'
        maxHeight: 150
        maxWidth: 150
        aspect: '3:2'
      ,
        suffix: '-thumb1'
        maxHeight: 250
        maxWidth: 250
        aspect: '1:1'
      ]

    Upload = require 's3-uploader'

Configure [s3-uploader](https://github.com/Turistforeningen/node-s3-uploader) to
store the following versions:

| Suffix    | Max Height | Max Width | Aspect Ratio |
|-----------|------------|-----------|--------------|
| `-full`   | `1200`     | `1200`    |              |
| `-800`    | `800`      | `800`     | `3:2`        |
| `-500`    | `500`      | `500`     | `3:2`        |
| `-260`    | `260`      | `260`     | `3:2`        |
| `-150`    | `150`      | `150`     | `3:2`        |
| `-thumb1` | `250`      | `250`     | `1:1`        |
| `-thumb2` | `150`      | `150`     | `1:1`        |

    module.exports = new Upload process.env.AWS_BUCKET_NAME,
      aws:
        region: process.env.AWS_BUCKET_REGION
        path: process.env.AWS_BUCKET_PATH
        acl: 'public-read'
        httpOptions: timeout: 60000

      returnExif: true

      cleanup:
        original: true
        versions: true

      original:
        awsImageAcl: 'private'

      versions: [
        suffix: '-full'
        quality: 80
        maxHeight: 1200
        maxWidth: 1200
      ,
        suffix: '-800'
        maxHeight: 800
        maxWidth: 800
        aspect: '3:2'
      ,
        suffix: '-500'
        maxHeight: 500
        maxWidth: 500
        aspect: '3:2'
      ,
        suffix: '-260'
        maxHeight: 260
        maxWidth: 260
        aspect: '3:2'
      ,
        suffix: '-150'
        maxHeight: 150
        maxWidth: 150
        aspect: '3:2'
      ,
        suffix: '-thumb1'
        maxHeight: 250
        maxWidth: 250
        aspect: '1:1'
      ]