<commit_before>import { EventEmitter } from 'events';

import Router from './router';
import Model from './model';
import Adapter from 'restle-memory';

import express from 'express';
import bodyParser from 'body-parser';
import _ from 'lodash';

import parseModelName from './utils/parse-model-name';

/**
 * Restle is a database-agnostic, JSON API engine. Restle handles data objects
 * and the relationships between them. Restle has an in-memory adapter baked in,
 * which means you can begin creating a Restle app without a database in mind.
 *
 * ```js
 * const Restle = require('restle');
 * const app = new Restle({
 *   port: 3000,
 *   namespace: 'api',
 * });
 *
 * app.on('ready', () => console.log('Listening on port ${app.port}'));
 * ```
 *
 * @class Restle
 * @constructor
 * @param {Object} options
 */
export default class Restle extends EventEmitter {
  constructor(options = {}) {
    if (typeof options !== 'object')
      throw new TypeError(`Argument "options" must be an object.`);

    super();

    // FIXME: hacky
    options.restle = this;

    // instantiate models hash
    this.models = {};

    Object.assign(this, {
      adapter: options.adapter || new Adapter(),
      router: new Router(options),
      app: express(),
      namespace: options.namespace ? `/${options.namespace}` : '',
      port: options.port || 3000,
      cors: options.cort || '*',
    });

    // body parsing middleware
    this.app.use(bodyParser.json({ type: 'application/*+json' }));
    this.app.use(bodyParser.urlencoded({ extended: true }));

    // configure cors
    this.app.use((req, res, next) => {
      const cors = this.cors;
      const origin = req.get('origin');

      if (!cors) return next();

      const isOriginAllowed = cors === '*' ||
        (Array.isArray(cors) ? cors.indexOf(origin) > -1 : origin === cors);

      if (isOriginAllowed) {
        const allowedHeaders = [
          'Origin',
          'X-Requested-With',
          'Content-Type',
          'Accept',
          'Authorization',
        ];

        const allowedMethods = [
          'OPTIONS',
          'GET',
          'POST',
          'PATCH',
          'DELETE',
        ];

        res.set('access-control-allow-origin', origin);
        res.set('access-control-allow-headers', allowedHeaders);
        res.set('access-control-allow-methods', allowedMethods);
      }

      if (req.method === 'OPTIONS') return res.sendStatus(isOriginAllowed ? 200 : 403);

      return next();
    });

    // use the router
    this.app.use(`${this.namespace}`, this.router.router);

    // start express app
    this.server = this.app.listen(this.port);
  }

  /**
   * Asynchronously disconnect the HTTP server and database adapters.
   *
   * @return {Promise}
   */
  disconnect() {
    return new Promise((resolve, reject) => {
      // close express server
      this.server.close();

      // disconnect the adapter
      this.adapter.disconnect().then(() => {
        this.emit('disconnected');
        return resolve();
      }).catch(err => {
        return reject(err);
      });
    });
  }

  /**
   * This method registers a model of type `name` with `schema`.
   *
   * @todo Refactor into single loop.
   * @todo Add adapter connections to a Promise array.
   * @todo Remove lodash dependency.
   * @param {String} name
   * @param {Object} schema
   * @param {Adapter} adapter
   * @return {Promise}
   */
  register(schemas) {
    let adapters = [];
    adapters.push(this.adapter.connect());

    for (let name in schemas) {
      let adapter = this.adapter;
      let schema = schemas[name];
      let type = parseModelName(name);

      // override default adapter
      if (schema.options && schema.options.adapter) {
        adapter = schema.options.adapter;
        adapters.push(adapter.connect());
      }

      let model = Model.create(name, schema, adapter);

      // populate created model relationships
      this.models[type] = model;

      _.each(model.relationships, relationship => {
        _.forOwn(this.models, (value, key) => {
          if (value.type === type) return;
<commit_msg>Rework disconnect method to handle many adapters<commit_after>import { EventEmitter } from 'events';

import Router from './router';
import Model from './model';
import Adapter from 'restle-memory';

import express from 'express';
import bodyParser from 'body-parser';
import _ from 'lodash';

import parseModelName from './utils/parse-model-name';

/**
 * Restle is a database-agnostic, JSON API engine. Restle handles data objects
 * and the relationships between them. Restle has an in-memory adapter baked in,
 * which means you can begin creating a Restle app without a database in mind.
 *
 * ```js
 * const Restle = require('restle');
 * const app = new Restle({
 *   port: 3000,
 *   namespace: 'api',
 * });
 *
 * app.on('ready', () => console.log('Listening on port ${app.port}'));
 * ```
 *
 * @class Restle
 * @constructor
 * @param {Object} options
 */
export default class Restle extends EventEmitter {
  constructor(options = {}) {
    if (typeof options !== 'object')
      throw new TypeError(`Argument "options" must be an object.`);

    super();

    // FIXME: hacky
    options.restle = this;

    // instantiate models hash
    this.models = {};

    Object.assign(this, {
      adapter: options.adapter || new Adapter(),
      router: new Router(options),
      app: express(),
      namespace: options.namespace ? `/${options.namespace}` : '',
      port: options.port || 3000,
      cors: options.cors || '*',
    });

    // body parsing middleware
    this.app.use(bodyParser.json({ type: 'application/*+json' }));
    this.app.use(bodyParser.urlencoded({ extended: true }));

    // configure cors
    // TODO: make cors more configurable
    this.app.use((req, res, next) => {
      const cors = this.cors;
      const origin = req.get('origin');

      if (!cors)
        return next();

      const isOriginAllowed = cors === '*' ||
        (Array.isArray(cors) ? cors.indexOf(origin) > -1 : origin === cors);

      if (isOriginAllowed) {
        const allowedHeaders = [
          'Origin',
          'X-Requested-With',
          'Content-Type',
          'Accept',
          'Authorization',
        ];

        const allowedMethods = [
          'OPTIONS',
          'GET',
          'POST',
          'PATCH',
          'DELETE',
        ];

        res.set('access-control-allow-origin', origin);
        res.set('access-control-allow-headers', allowedHeaders);
        res.set('access-control-allow-methods', allowedMethods);
      }

      if (req.method === 'OPTIONS')
        return res.sendStatus(isOriginAllowed ? 200 : 403);

      return next();
    });

    // use the router
    this.app.use(`${this.namespace}`, this.router.router);

    // start express app
    this.server = this.app.listen(this.port);
  }

  /**
   * Asynchronously disconnect the HTTP server and database adapters.
   *
   * @return {Promise}
   */
  disconnect() {
    const models = this.models;
    const adapters = [];

    return new Promise((resolve, reject) => {
      // close express server
      this.server.close();

      for (let model in models)
        adapters.push(models[model].adapter.disconnect());

      Promise.all(adapters).then(() => {
        this.emit('disconnected');
        return resolve();
      }).catch(err => {
        return reject(err);
      });
    });
  }

  /**
   * This method registers a model of type `name` with `schema`.
   *
   * @todo Refactor into single loop.
   * @todo Add adapter connections to a Promise array.
   * @todo Remove lodash dependency.
   * @param {Object} schemas
   * @return {Promise}
   */
  register(schemas) {
    let adapters = [];
    adapters.push(this.adapter.connect());

    for (let name in schemas) {
      let adapter = this.adapter;
      let schema = schemas[name];
      let type = parseModelName(name);

      // override default adapter
      if (schema.options && schema.options.adapter) {
        adapter = schema.options.adapter;
        adapters.push(adapter.connect());
      }

      let model = Model.create(name, schema, adapter);

      // populate created model relationships
      this.models[type] = model;

      _.each(model.relationships, relationship => {
        _.forOwn(this.models, (value, key) => {
          if (value.type === type) return;

{'fraction_non_alphanumeric': 0.14507092621289785, 'fraction_numerical': 0.0035761115746811302, 'mean_word_length': 2.4784411276948592, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, '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': '1296021', 'n_tokens_mistral': 2497, 'n_tokens_neox': 2419, 'n_words': 661}