<commit_before>module.exports = class Router

  ##
  # Config
  #   - errorHandler: function to correctly handle error
  #   - paths
  #     - entryPath (required)
  #     - routes (optional)
  #     - controllerDir (optional)
  #   - stashPerf: optional function to store performance stats
  #   - stashError: optional function to notify server of error
  ##
  config: null

  # Internally stored route definitions.
  _routes: null

  constructor: (config, callback) ->
    @_routes = []
    @initialize(config, callback)

  initialize: (config, callback) ->
    @config = config || {}
    @config.paths ||= {}
    callback ||= ->

    if !config.paths.entryPath
      return callback(new Error("Missing entryPath"))
    config.paths.routes ||= config.paths.entryPath + '/routes'
    config.paths.controllerDir ||= config.paths.entryPath + '/controllers'
    callback()

  getController: (controllerName) ->
   require("#{@config.paths.controllerDir}/#{controllerName}_controller")

  # Given an object with 'controller' and 'action' properties,
  # return the corresponding action function.
  getAction: (spec) ->
    controller = @getController(spec.controller)
    controller[spec.action]

  # This is the method that renders the request.
  getHandler: (action, routeInfo) ->
    router = @

    (req, res, next) ->
      context =
        app: req.rendrApp
        redirectTo: (url) -> res.redirect(url)

      params = req.query || {}
      req.route.keys.forEach (routeKey) ->
        params[routeKey.name] = req.route.params[routeKey.name]

      start = new Date
      action.call context, params, (err, template, data) ->
        router.stashPerf(req, "data", new Date - start)
        return router.handleErr(err, req, res) if err

        start = new Date
        viewData =
          locals: data
          app: req.rendrApp
          req: req
        res.render template, viewData, (err, html) ->
          return router.handleErr(err, req, res) if err

          # Set any headers based on route.
          res.set(router.getHeadersForRoute(routeInfo))

          res.type('html').end(html)
          router.stashPerf(req, "render", new Date - start)


  ##
  # Handle an error that happens while executing an action.
  # Could happen during the controller action, view rendering, etc.
  ##
  handleErr: (err, req, res) ->
    @stashError(req, err)
    if @config.errorHandler
      @config.errorHandler(err, req, res)
    else
      # default error handler
      if @config.dumpExceptions
        text = "Error: #{err.message}\n"
        text += "\nStack:\n #{err.stack}" if err.stack
      else
        text = "500 Internal Server Error"
      res.status(err.status || 500)
      res.type('text').send(text)


  getHeadersForRoute: (routeInfo) ->
    headers = {}
    if routeInfo.maxAge?
      headers['Cache-Control'] = "public, max-age=#{routeInfo.maxAge}"
    headers

  # stash performance metrics, if handler available
  stashPerf: (req, name, value) ->
    if @config.stashPerf?
      @config.stashPerf(req, name, value)

  # stash error, if handler available
  stashError: (req, err) ->
    if @config.stashError?
      @config.stashError(req, err)

  # Build route definitions based on the routes file.
  buildRoutes: ->
    routeBuilder = require(@config.paths.routes)
    routeBuilder(@route)
    @routes()

  # Returns current route definitions.
  routes: ->
    @_routes.slice()

  # Method passed to routes file to build up routes definition.
  # Adds a single route definition.
  route: (pattern, definitions...) =>
    definition = @parseDefinitions(definitions)
    action = @getAction(definition)<commit_msg>Router: consistent terminology 'definition'<commit_after>module.exports = class Router

  ##
  # Config
  #   - errorHandler: function to correctly handle error
  #   - paths
  #     - entryPath (required)
  #     - routes (optional)
  #     - controllerDir (optional)
  #   - stashPerf: optional function to store performance stats
  #   - stashError: optional function to notify server of error
  ##
  config: null

  # Internally stored route definitions.
  _routes: null

  constructor: (config, callback) ->
    @_routes = []
    @initialize(config, callback)

  initialize: (config, callback) ->
    @config = config || {}
    @config.paths ||= {}
    callback ||= ->

    if !config.paths.entryPath
      return callback(new Error("Missing entryPath"))
    config.paths.routes ||= config.paths.entryPath + '/routes'
    config.paths.controllerDir ||= config.paths.entryPath + '/controllers'
    callback()

  getController: (controllerName) ->
   require("#{@config.paths.controllerDir}/#{controllerName}_controller")

  # Given an object with 'controller' and 'action' properties,
  # return the corresponding action function.
  getAction: (definition) ->
    controller = @getController(definition.controller)
    controller[definition.action]

  # This is the method that renders the request.
  getHandler: (action, definition) ->
    router = @

    (req, res, next) ->
      context =
        app: req.rendrApp
        redirectTo: (url) -> res.redirect(url)

      params = req.query || {}
      req.route.keys.forEach (routeKey) ->
        params[routeKey.name] = req.route.params[routeKey.name]

      start = new Date
      action.call context, params, (err, template, data) ->
        router.stashPerf(req, "data", new Date - start)
        return router.handleErr(err, req, res) if err

        start = new Date
        viewData =
          locals: data
          app: req.rendrApp
          req: req
        res.render template, viewData, (err, html) ->
          return router.handleErr(err, req, res) if err

          # Set any headers based on route.
          res.set(router.getHeadersForRoute(definition))

          res.type('html').end(html)
          router.stashPerf(req, "render", new Date - start)


  ##
  # Handle an error that happens while executing an action.
  # Could happen during the controller action, view rendering, etc.
  ##
  handleErr: (err, req, res) ->
    @stashError(req, err)
    if @config.errorHandler
      @config.errorHandler(err, req, res)
    else
      # default error handler
      if @config.dumpExceptions
        text = "Error: #{err.message}\n"
        text += "\nStack:\n #{err.stack}" if err.stack
      else
        text = "500 Internal Server Error"
      res.status(err.status || 500)
      res.type('text').send(text)


  getHeadersForRoute: (definition) ->
    headers = {}
    if definition.maxAge?
      headers['Cache-Control'] = "public, max-age=#{definition.maxAge}"
    headers

  # stash performance metrics, if handler available
  stashPerf: (req, name, value) ->
    if @config.stashPerf?
      @config.stashPerf(req, name, value)

  # stash error, if handler available
  stashError: (req, err) ->
    if @config.stashError?
      @config.stashError(req, err)

  # Build route definitions based on the routes file.
  buildRoutes: ->
    routeBuilder = require(@config.paths.routes)
    routeBuilder(@route)
    @routes()

  # Returns current route definitions.
  routes: ->
    @_routes.slice()

  # Method passed to routes file to build up routes definition.
  # Adds a single route definition.
  route: (pattern, definitions...) =>
    definition = @parseDefinitions(definitions)
    action = @getAction(definition)
{'fraction_non_alphanumeric': 0.12004360267066358, 'fraction_numerical': 0.0016351001498841804, 'mean_word_length': 2.7602459016393444, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7619019', 'n_tokens_mistral': 2185, 'n_tokens_neox': 2136, 'n_words': 596}