<commit_before>var path = require('path');
var ApplicationError = require('./lib/application_error');
var l = require('./lib/log')('ravel');
var rest = require('./lib/rest');

module.exports = function() {  
  var Ravel = {
    modules: {}
  };
  
  var moduleFactories = {};
  var serviceFactories = {};
  var knownParameters = {};
  var params = {};
  var injector = require('./lib/injector')(Ravel, moduleFactories, module.parent);
  
  //Change __dirname to current working directory of the
  //app using the ravel library, so that modules can be
  //loaded with relative paths.
  __dirname = process.cwd();
  
  /**
   * Register a parameter
   * @param {String} key the key for the parameter
   * @param {Boolean | undefined} required true, iff the parameter is required
   */
  Ravel.registerSimpleParameter = function(key, required) {
    knownParameters[key] = {
      required: required
    }
  };
  
  /**
   * Get a parameter
   *
   * @param {String} key the key for the parameter
   * @throws ApplicationError.NotFound if the parameter is required and not set
   * @return {? | undefined} the parameter value, or undefined if it is not required and not set
   */
  Ravel.get = function(key) {
    if (!knownParameters[key]) {
      throw new ApplicationError.NotFound('Parameter \'' + name + '\' was requested, but is unknown.');
    } else if (knownParameters[key].required && params[key] === undefined) {
      throw new ApplicationError.NotFound('Known required parameter \'' + key + '\' was requested, but hasn\'t been defined yet.');
    } else if (params[key] === undefined) {
      l.w('Optional parameter \'' + key + '\' was requested, but is not defined.');
      return undefined;
    } else {
      return params[key];
    }
  };
  
  /**
   * Set a parameter
   *
   * @param {String} key the key for the parameter
   * @param {?} value the value for the parameter
   * @throws ApplicationError.IllegalValueError if key refers to an unregistered parameter
   * @return {?} the parameter value
   */
  Ravel.set = function(key, value) {
    if (knownParameters[key]) {
      params[key] = value;
    } else {
      throw new ApplicationError.IllegalValueError('Parameter \'' + key + '\' is not supported.');
    }
  };
  
  /**
   * Register a module with Ravel
   *
   * A module is a pure node.js javascript API consisting of functions with no
   * network-related functionality, suitable for unit-testing.
   *
   * Module should use injection to get what it needs
   *
   * @param {String} name The name of the module
   * @param {String} modulePath The path to the module   
   * 
   */
  Ravel.module = function(name, modulePath) {
    //if a module with this name has already been regsitered, error out
    if (moduleFactories[name]) {
      throw new ApplicationError.DuplicateEntryError('Module with name \'' + name + '\' has already been registered.');
    }

    var module = {};
    var methodBuilder = {      
      add: function(methodName, handler) {
        if (module[methodName]) {
          throw new ApplicationError.DuplicateEntryError('Method with name \'' + methodName + '\' has already been registered.');
        }
        var transactionalMethodName = 't' + methodName[0].toUpperCase() + methodName.slice(1,methodName.length);
        module[transactionalMethodName] = handler;
        module[methodName] = Ravel.db.createTransactionEntryPoint(module[transactionalMethodName]);
      }
    };

    //save uninitialized module to Ravel.modules
    //so that it can be injected into other 
    //modules and lazily instantiated
    Ravel.modules[name] = module;

    //build module instantiation function
    moduleFactories[name] = function() {
      var moduleInject = require(path.join(__dirname, modulePath));
      injector.inject({
        '$L': require('./lib/log')(name),
        '$MethodBuilder': methodBuilder,
        '$KV': Ravel.kvstore
      },moduleInject);
    }
  };
  
  /**
   * Register a service with Ravel
   *
   * A service is a RESTful endpoint for a single Resource
   *
   * @param {String} basePath The base path of the all the Resource's endpoints
   * @param {String} servicePath the path of the service module to require(...)
   *
   */
  Ravel.service = function(basePath, servicePath) {
    //if a service with this name has already been regsitered, error out
    if (serviceFactories[basePath]) {
      throw new ApplicationError.DuplicateEntryError('Service with name \'' + name + '\' has already been registered.');
    }
    var endpointBuilder = {
      _methods: {}
    };
    var addMethod = function(method) {
      endpointBuilder[method] = function(secure, middleware) {
        if (endpointBuilder._methods[method]) {
          throw new ApplicationError.DuplicateEntryError('Method '+method+' has already been registered with service \''+serviceName+'\'');
        }
        endpointBuilder._methods[method] = {
          secure: secure,
          middleware: middleware
        };
        return endpointBuilder;
      }
    };    
    addMethod('getAll');
    addMethod('postAll');
    addMethod('putAll');
    addMethod('deleteAll');    
    addMethod('get');
    addMethod('post');
    addMethod('put');
    addMethod('delete');

    //build service instantiation function
    serviceFactories[basePath] = function(expressApp) {
      var serviceInject = require(path.join(__dirname, servicePath));
      injector.inject({
        '$L': require('./lib/log')(basePath), 
        '$EndpointBuilder': endpointBuilder,
        '$Rest': rest,
        '$KV': Ravel.kvstore
      }, serviceInject);
      //process all methods and add to express app
      var buildRoute = function(methodType, methodName) {
        var bp = basePath;
        if (methodName === 'get' || methodName === 'post' || methodName === 'put' || methodName === 'delete') {
          bp = path.join(basePath, '/:id');
        }
        //TODO integrate simple rest, give middleware a callback
        if (endpointBuilder._methods[methodName]) {
          if (endpointBuilder._methods[methodName].secure) {
            l.i('Registering secure service endpoint ' + methodType.toUpperCase() + ' ' + bp);
            expressApp[methodType](bp, Ravel.authorize, function(req, res) {
              endpointBuilder._methods[methodName].middleware(req, res);
            });
          } else {
            l.i('Registering public service endpoint ' + methodType.toUpperCase() + ' ' + bp);
            expressApp[methodType](bp, function(req, res) {
              endpointBuilder._methods[methodName].middleware(req, res);
            });
          }
        } else {
          //l.i('Registering unimplemented service endpoint ' + methodType.toUpperCase() + ' ' + bp);
          expressApp[methodType](bp, function(req, res) {
            res.status(rest.NOT_IMPLEMENTED).end();
          });
        }
      };
      buildRoute('get', 'getAll');
      buildRoute('post', 'postAll');
      buildRoute('put', 'putAll');
      buildRoute('delete', 'deleteAll');
      buildRoute('get', 'get');
      buildRoute('post', 'post');
      buildRoute('put', 'put');
      buildRoute('delete', 'delete');
    }
  };
  
  /**
   * Start the application
   */<commit_msg>Service's methods can now be passed an unlimited number of middleware functions, instead of just one.<commit_after>var path = require('path');
var ApplicationError = require('./lib/application_error');
var l = require('./lib/log')('ravel');
var rest = require('./lib/rest');

module.exports = function() {  
  var Ravel = {
    modules: {}
  };
  
  var moduleFactories = {};
  var serviceFactories = {};
  var knownParameters = {};
  var params = {};
  var injector = require('./lib/injector')(Ravel, moduleFactories, module.parent);
  
  //Change __dirname to current working directory of the
  //app using the ravel library, so that modules can be
  //loaded with relative paths.
  __dirname = process.cwd();
  
  /**
   * Register a parameter
   * @param {String} key the key for the parameter
   * @param {Boolean | undefined} required true, iff the parameter is required
   */
  Ravel.registerSimpleParameter = function(key, required) {
    knownParameters[key] = {
      required: required
    }
  };
  
  /**
   * Get a parameter
   *
   * @param {String} key the key for the parameter
   * @throws ApplicationError.NotFound if the parameter is required and not set
   * @return {? | undefined} the parameter value, or undefined if it is not required and not set
   */
  Ravel.get = function(key) {
    if (!knownParameters[key]) {
      throw new ApplicationError.NotFound('Parameter \'' + name + '\' was requested, but is unknown.');
    } else if (knownParameters[key].required && params[key] === undefined) {
      throw new ApplicationError.NotFound('Known required parameter \'' + key + '\' was requested, but hasn\'t been defined yet.');
    } else if (params[key] === undefined) {
      l.w('Optional parameter \'' + key + '\' was requested, but is not defined.');
      return undefined;
    } else {
      return params[key];
    }
  };
  
  /**
   * Set a parameter
   *
   * @param {String} key the key for the parameter
   * @param {?} value the value for the parameter
   * @throws ApplicationError.IllegalValueError if key refers to an unregistered parameter
   * @return {?} the parameter value
   */
  Ravel.set = function(key, value) {
    if (knownParameters[key]) {
      params[key] = value;
    } else {
      throw new ApplicationError.IllegalValueError('Parameter \'' + key + '\' is not supported.');
    }
  };
  
  /**
   * Register a module with Ravel
   *
   * A module is a pure node.js javascript API consisting of functions with no
   * network-related functionality, suitable for unit-testing.
   *
   * Module should use injection to get what it needs
   *
   * @param {String} name The name of the module
   * @param {String} modulePath The path to the module   
   * 
   */
  Ravel.module = function(name, modulePath) {
    //if a module with this name has already been regsitered, error out
    if (moduleFactories[name]) {
      throw new ApplicationError.DuplicateEntryError('Module with name \'' + name + '\' has already been registered.');
    }

    var module = {};
    var methodBuilder = {      
      add: function(methodName, handler) {
        if (module[methodName]) {
          throw new ApplicationError.DuplicateEntryError('Method with name \'' + methodName + '\' has already been registered.');
        }
        var transactionalMethodName = 't' + methodName[0].toUpperCase() + methodName.slice(1,methodName.length);
        module[transactionalMethodName] = handler;
        module[methodName] = Ravel.db.createTransactionEntryPoint(module[transactionalMethodName]);
      }
    };

    //save uninitialized module to Ravel.modules
    //so that it can be injected into other 
    //modules and lazily instantiated
    Ravel.modules[name] = module;

    //build module instantiation function
    moduleFactories[name] = function() {
      var moduleInject = require(path.join(__dirname, modulePath));
      injector.inject({
        '$L': require('./lib/log')(name),
        '$MethodBuilder': methodBuilder,
        '$KV': Ravel.kvstore
      },moduleInject);
    }
  };
  
  /**
   * Register a service with Ravel
   *
   * A service is a RESTful endpoint for a single Resource
   *
   * @param {String} basePath The base path of the all the Resource's endpoints
   * @param {String} servicePath the path of the service module to require(...)
   *
   */
  Ravel.service = function(basePath, servicePath) {
    //if a service with this name has already been regsitered, error out
    if (serviceFactories[basePath]) {
      throw new ApplicationError.DuplicateEntryError('Service with name \'' + name + '\' has already been registered.');
    }
    var endpointBuilder = {
      _methods: {}
    };
    var addMethod = function(method) {
      endpointBuilder[method] = function() {
        //first argument is whether or not the endpoint requires authentication
        var secure = arguments[0];
        //all other arguments are express middleware of the form function(req, res, next?)
        var middleware = Array.prototype.slice.call(arguments, 1);
        if (endpointBuilder._methods[method]) {
          throw new ApplicationError.DuplicateEntryError('Method '+method+' has already been registered with service \''+serviceName+'\'');
        }
        endpointBuilder._methods[method] = {
          secure: secure,
          middleware: middleware
        };
        return endpointBuilder;
      }
    };    
    addMethod('getAll');
    addMethod('postAll');
    addMethod('putAll');
    addMethod('deleteAll');    
    addMethod('get');
    addMethod('post');
    addMethod('put');
    addMethod('delete');

    //build service instantiation function
    serviceFactories[basePath] = function(expressApp) {
      var serviceInject = require(path.join(__dirname, servicePath));
      injector.inject({
        '$L': require('./lib/log')(basePath), 
        '$EndpointBuilder': endpointBuilder,
        '$Rest': rest,
        '$KV': Ravel.kvstore
      }, serviceInject);
      //process all methods and add to express app
      var buildRoute = function(methodType, methodName) {
        var bp = basePath;
        if (methodName === 'get' || methodName === 'post' || methodName === 'put' || methodName === 'delete') {
          bp = path.join(basePath, '/:id');
        }
        var args = [bp];
        if (endpointBuilder._methods[methodName]) {
          if (endpointBuilder._methods[methodName].secure) {
            l.i('Registering secure service endpoint ' + methodType.toUpperCase() + ' ' + bp);
            args.push(Ravel.authorize);
            args = args.concat(endpointBuilder._methods[methodName].middleware);            
          } else {
            l.i('Registering public service endpoint ' + methodType.toUpperCase() + ' ' + bp);
            args = args.concat(endpointBuilder._methods[methodName].middleware);            
          }
          expressApp[methodType].apply(expressApp, args);
        } else {
          //l.i('Registering unimplemented service endpoint ' + methodType.toUpperCase() + ' ' + bp);
          expressApp[methodType](bp, function(req, res) {
            res.status(rest.NOT_IMPLEMENTED).end();
          });
        }
      };
      buildRoute('get', 'getAll');
      buildRoute('post', 'postAll');
      buildRoute('put', 'putAll');
      buildRoute('delete', 'deleteAll');
      buildRoute('get', 'get');
      buildRoute('post', 'post');
      buildRoute('put', 'put');
      buildRoute('delete', 'delete');
    }
  };
  
  /**
   * Start the application
   */
{'fraction_non_alphanumeric': 0.12785636561479868, 'fraction_numerical': 0.00040805223068552776, 'mean_word_length': 2.758946830265849, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '1473732', 'n_tokens_mistral': 4067, 'n_tokens_neox': 4037, 'n_words': 1168}