docs: function initAppDir (config, module, appDir, appSubDir) {
      // if called without sub dir then start with app dir
      if (!appSubDir) {
          appSubDir = appDir
      }
      // debug
      debug('initAppDir %s', appSubDir)
      // get relative path from app base to directory which determines
      // where in the routes tree this directory sits
      var rel = path.relative(appDir, appSubDir)
      // get route node from relative path
      var route = routeFromRel(module.routes, rel)
      // prevent infinite loops
      if (route.loaded) {
          return
      }
      // mark route as loaded
      route.loaded = true
      // directory names map
      var dirNames = {}
      // list of all sub-directories in directory
      var dirs = []
      // list of all files in directory
      var files = []
      // iterate over file names in directory
      _.each(fs.readdirSync(appSubDir), file => {
          // get absolute file
          var absFile = appSubDir+'/'+file
          // get stat of file to check if it is dir or file
          var stat = fs.statSync(absFile)
          // file
          if (stat.isFile()) {
              files.push(absFile)
          }
          // directory
          else if (stat.isDirectory()) {
              // add name to map for doing conflict checking
              dirNames[file] = true
              // add abs path to list for recursive loading
              dirs.push(absFile)
          }
          // ignore anything else
          else {
              debug('initAppDir: ignoring %s', absFile)
          }
      })
      // recursively process all sub-directories - go depth first so that routes
      // for sub-dirs are added before wildcard path param matching rules
      _.each(dirs, dir => {
          initAppDir(config, module, appDir, dir)
      })
      // regular expression for matching controller files
      var controllerRegEx = /controller\.js$/
      // regular expression for matching model files
      var modelRegEx = /model\.js$/
      // regular expression for matching template files extname starts with .
      config.handlebars.templateRegExp = new RegExp('\\'+config.handlebars.ext+'$')
      // ImmutableCoreController for node - multiple files will be merged
      route.controller = {}
      // ImmutableCoreModel for node - multiple files will be merged
      route.model = {}
      // list of templates indexed by name. name of template file indicates path
      // that it should be served on. if path does not have a controller then
      // a default controller will be created.
      route.templates = {}
      // get mapping of templates by path
      route.templatesInverse = {}
      // require all controller and model files for route
      _.each(files, file => {
          // get type
          var type
          // controllers must be named controller.js
          if (file.match(controllerRegEx)) {
              // require controller file
              var controller = require(path.relative(__dirname, file))
              // require defined
              assert.ok(defined(controller), `nothing exported for ${file}`)
              // throw error if controller instantiated
              assert.ok(!controller.ImmutableCoreController, `controller must be plain object ${file}`)
              // merge controller spec
              _.merge(route.controller, controller)
          }
          // models must be named model.js
          else if (file.match(modelRegEx)) {
              // require model file
              var model = require(path.relative(__dirname, file))
              // require defined
              assert.ok(defined(model), `nothing exported for ${file}`)
              // throw error if controller instantiated
              assert.ok(!model.ImmutableCoreModel, `model must be plain object ${file}`)
              // merge model spec
              _.merge(route.model, model)
          }
          // template files must end in hbs
          else if (file.match(config.handlebars.templateRegExp)) {
              // get only the file name
              file = path.basename(file)
              // remove extension from file
              file = file.replace(config.handlebars.templateRegExp, '')
              // template path used in render call is relative path from app
              // base (if any) and the file name without the extensions
              var templatePath = (rel.length ? rel+'/' : '') + file
              // split file name on dot - if template name has a dot then the
              // part after the dot must be the role that the template is for
              var fileParts = file.split('.')
              // if there is only a single part then not a role specific template
              if (fileParts.length === 1) {
                  // if entry does not exist for template name then create it
                  if (!defined(route.templates[file])) {
                      route.templates[file] = {
                          role: {},
                      }
                  }
                  // store template name and template path under default all role
                  route.templates[file].role.all = templatePath
                  // create inverse entry keyed by full path
                  route.templatesInverse[templatePath] = {
                      name: file,
                      role: 'all',
                  }
              }
              // it there are two parts they must be name.role
              else if (fileParts.length === 2) {
                  var name = fileParts[0]
                  var role = fileParts[1]
                  // if entry does not exist for template name then create it
                  if (!defined(route.templates[name])) {
                      route.templates[name] = {
                          role: {},
                      }
                  }
                  // store template name and template path
                  route.templates[name].role[role] = templatePath
                  // create inverse entry keyed by full path
                  route.templatesInverse[templatePath] = {
                      name: name,
                      role: role,
                  }
              }
              // if there are not 1 or 2 parts this is an error
              else {
                  throw new Error('invalid template file name '+file)
              }
          }
          // otherwise ignore file
          else {
              return
          }
      })
      // if controller name is not set construct from module name
      if (!defined(route.controller.name)) {
          // get controller name as app + path
          route.controller.name = changeCase.camelCase(module.name)
              + changeCase.pascalCase(rel.length ? rel : 'Index')
      }
      // if controller path is not set use current relative path
      if (!defined(route.controller.path)) {
          route.controller.path = rel
      }
  }