<commit_before>const path = require('path');
const requireg = require('requireg');
const Helper = require('../helper');
const REST = require('./REST');

/**
 * Helper for managing remote data using REST API.
 * Uses data generators like [rosie](https://github.com/rosiejs/rosie) or factory girl to create new record.
 *
 * By defining a factory you set the rules of how data is generated.
 * This data will be saved on server via REST API and deleted in the end of a test.
 *
 * ## Use Case
 *
 * Acceptance tests interact with a websites using UI and real browser.
 * There is no way to create data for a specific test other than from user interface.
 * That makes tests slow and fragile. Instead of testing a single feature you need to follow all creation/removal process.
 *
 * This helper solves this problem.
 * Most of web application have API, and it can be used to create and delete test records.
 * By combining REST API with Factories you can easily create records for tests:
 *
 * ```js
 * I.have('user', { login: 'davert', email: 'davert@mail.com' });
 * let id = await I.have('post', { title: 'My first post'});
 * I.haveMultiple('comment', 3, {post_id: id});
 * ```
 *
 * To make this work you need
 *
 * 1. REST API endpoint which allows to perform create / delete requests and
 * 2. define data generation rules
 *
 * ### Setup
 *
 * Install [Rosie](https://github.com/rosiejs/rosie) and [Faker](https://www.npmjs.com/package/faker) libraries.
 *
 * ```sh
 * npm i rosie faker --save-dev`
 * ```
 *
 * Create a factory file for a resource.
 *
 * See the example for Posts factories:
 *
 * ```js
 * // tests/factories/posts.js
 *
 * var Factory = require('rosie').Factory;
 * var faker = require('faker');
 *
 * module.exports = new Factory()
 *    // no need to set id, it will be set by REST API
 *    .attr('author', () => faker.name.findName())
 *    .attr('title', () => faker.lorem.sentence())
 *    .attr('body', () => faker.lorem.paragraph());
 * ```
 * For more options see [rosie documentation](https://github.com/rosiejs/rosie).
 *
 * Then configure ApiDataHelper to match factories and REST API:

 * ### Configuration
 *
 * ApiDataFactory has following config options:
 *
 * * `endpoint`: base URL for the API to send requests to.
 * * `cleanup` (default: true): should inserted records be deleted up after tests
 * * `factories`: list of defined factories
 * * `returnId` (default: false): return id instead of a complete response when creating items.
 * * `headers`: list of headers
 * * `REST`: configuration for REST requests
 *
 * See the example:
 *
 * ```js
 *  ApiDataFactory: {
 *    endpoint: "http://user.com/api",
 *    cleanup: true,
 *    headers: {
 *      'Content-Type': 'application/json',
 *      'Accept': 'application/json',
 *    },
 *    factories: {
 *      post: {
 *        uri: "/posts",
 *        factory: "./factories/post",
 *      },
 *      comment: {
 *        factory: "./factories/comment",
 *        create: { post: "/comments/create" },
 *        delete: { post: "/comments/delete/{id}" },
 *        fetchId: (data) => data.result.id
 *      }
 *    }
 * }
 * ```

 * It is required to set REST API `endpoint` which is the baseURL for all API requests.
 * Factory file is expected to be passed via `factory` option.
 *
 * This Helper uses [REST](http://codecept.io/helpers/REST/) helper and accepts its configuration in "REST" section.
 * For instance, to set timeout you should add:
 *
 * ```js
 * "ApiDataFactory": {
 *    "REST": {
 *      "timeout": "100000",
 *   }
 * }
 * ```
 *
 * ### Requests
 *
 * By default to create a record ApiDataFactory will use endpoint and plural factory name:
 *
 * * create: `POST {endpoint}/{resource} data`
 * * delete: `DELETE {endpoint}/{resource}/id`
 *
 * Example (`endpoint`: `http://app.com/api`):
 *
 * * create: POST request to `http://app.com/api/users`
 * * delete: DELETE request to `http://app.com/api/users/1`
 *
 * This behavior can be configured with following options:
 *
 * * `uri`: set different resource uri. Example: `uri: account` => `http://app.com/api/account`.
 * * `create`: override create options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/create" }`
 * * `delete`: override delete options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/delete/{id}" }`
 *
 * Requests can also be overridden with a function which returns [axois request config](https://github.com/axios/axios#request-config).
 *
 * ```js
 * create: (data) => ({ method: 'post', url: '/posts', data }),
 * delete: (id) => ({ method: 'delete', url: '/posts', data: { id } })
 *
 * ```
 *
 * Requests can be updated on the fly by using `onRequest` function. For instance, you can pass in current session from a cookie.
 *
 * ```js
 *  onRequest: async (request) => {
 *     // using global codeceptjs instance
 *     let cookie = await codeceptjs.container.helpers('WebDriver').grabCookie('session');
 *     request.headers = { Cookie: `session=${cookie.value}` };
 *   }
 * ```
 *
 * ### Responses
 *
 * By default `I.have()` returns a promise with a created data:
 *
 * ```js
 * let client = await I.have('client');
 * ```
 *
 * Ids of created records are collected and used in the end of a test for the cleanup.
 * If you need to receive `id` instead of full response enable `returnId` in a helper config:
 *
 * ```js
 * // returnId: false
 * let clientId = await I.have('client');
 * // clientId == 1
 *
 * // returnId: true
 * let clientId = await I.have('client');
 * // client == { name: 'John', email: 'john@snow.com' }
 * ```
 *
 * By default `id` property of response is taken. This behavior can be changed by setting `fetchId` function in a factory config.
 *
 *
 * ```js
 *    factories: {
 *      post: {
 *        uri: "/posts",
 *        factory: "./factories/post",
 *        fetchId: (data) => data.result.posts[0].id
 *      }
 *    }
 * ```
 *
 *
 * ## Methods
 */
class ApiDataFactory extends Helper {
  constructor(config) {
    super(config);

    const defaultConfig = {
      cleanup: true,
      REST: {},
      factories: {},
      returnId: false,
    };
    this.config = Object.assign(defaultConfig, this.config);

    if (this.config.headers) this.config.REST.defaultHeaders = this.config.headers;
    if (this.config.onRequest) this.config.REST.onRequest = this.config.onRequest;
    this.restHelper = new REST(Object.assign(this.config.REST, { endpoint: this.config.endpoint }));
    this.factories = this.config.factories;

    for (const factory in this.factories) {
      const factoryConfig = this.factories[factory];
      if (!factoryConfig.uri && !factoryConfig.create) {
        throw new Error(`Uri for factory "${factory}" is not defined. Please set "uri" parameter:

    "factories": {
      "${factory}": {
        "uri": ...
`);
      }

      if (!factoryConfig.create) factoryConfig.create = { post: factoryConfig.uri };
      if (!factoryConfig.delete) factoryConfig.delete = { delete: `${factoryConfig.uri}/{id}` };

      this.factories[factory] = factoryConfig;
    }

    this.created = {};
    Object.keys(this.factories).forEach(f => this.created[f] = []);
  }

  static _checkRequirements() {
    try {
      requireg('axios');
      requireg('rosie');
    } catch (e) {
      return ['axios', 'rosie'];
    }
  }

  _after() {
    if (!this.config.cleanup) {
      return Promise.resolve();
    }
    const promises = [];

    // clean up all created items
    for (const factoryName in this.created) {
      const createdItems = this.created[factoryName];
      if (!createdItems.length) continue;
      this.debug(`Deleting ${createdItems.length} ${factoryName}(s)`);
      for (const id in createdItems) {
        promises.push(this._requestDelete(factoryName, createdItems[id]));
      }
    }

    return Promise.all(promises);
  }

  /**
   * Generates a new record using factory and saves API request to store it.
   *
   * ```js
   * // create a user
   * I.have('user');
   * // create user with defined email
   * // and receive it when inside async function
   * const user = await I.have('user', { email: 'user@user.com'});
   * ```
   *
   * @param {*} factory factory to use
   * @param {*} params predefined parameters
   */
  have(factory, params) {
    const item = this._createItem(factory, params);
    this.debug(`Creating ${factory} ${JSON.stringify(item)}`);
    return this._requestCreate(factory, item);
  }

  /**
   * Generates bunch of records and saves multiple API requests to store them.
   *
   * ```js
   * // create 3 posts
   * I.haveMultiple('post', 3);
   *
   * // create 3 posts by one author
   * I.haveMultiple('post', 3, { author: 'davert' });
   * ```
   *
   * @param {*} factory
   * @param {*} times
   * @param {*} params
   */
  haveMultiple(factory, times, params) {
    const promises = [];
    for (let i = 0; i < times; i++) {
      promises.push(this.have(factory, params));
    }
    return Promise.all(promises);
  }

  _createItem(model, data) {
    if (!this.factories[model]) {
      throw new Error(`Factory ${model} is not defined in config`);
    }
    let modulePath = this.factories[model].factory;
    try {
      try {
        require.resolve(modulePath);
      } catch (e) {
        modulePath = path.join(global.codecept_dir, modulePath);
      }
      const builder = require(modulePath);
      return builder.build(data);
    } catch (err) {
      throw new Error(`Couldn't load factory file from ${modulePath}, check that

  "factories": {
    "${model}": {
      "factory": "./path/to/factory"

points to valid factory file.
Factory file should export an object with build method.

Current file error: ${err.message}`);
    }
  }

  _fetchId(body, factory) {
    if (this.config.factories[factory].fetchId) {
      return this.config.factories[factory].fetchId(body);
    }
    if (body.id) return body.id;
    if (body[factory] && body[factory].id) return body[factory].id;
    return null;
  }

  /**
   * Executes request to create a record in API.
   * Can be replaced from a in custom helper.
   *
   * @param {*} factory
   * @param {*} data
   */
  _requestCreate(factory, data) {
    let request = createRequestFromFunction(this.factories[factory].create, data);

    if (!request) {
      const method = Object.keys(this.factories[factory].create)[0];
      const url = this.factories[factory].create[method];
      request = {
        method,
        url,
        data,
      };
    }

    request.baseURL = this.config.endpoint;

    return this.restHelper._executeRequest(request).then((resp) => {
      const id = this._fetchId(resp.data, factory);
      this.created[factory].push(id);
      this.debugSection('Created', `Id: ${id}`);
      if (this.config.returnId) return id;
      return resp.data;
    });
  }

  /**
   * Executes request to delete a record in API
   * Can be replaced from a custom helper.
   *
   * @param {*} factory
   * @param {*} id
   */
  _requestDelete(factory, id) {
    if (!this.factories[factory].delete) return;
    let request = createRequestFromFunction(this.factories[factory].delete, id);

    if (!request) {
      const method = Object.keys(this.factories[factory].delete)[0];
      const url = this.factories[factory].delete[method].replace('{id}', id);
      request = {
        method,
        url,
      };
    }

    request.baseURL = this.config.endpoint;

    return this.restHelper._executeRequest(request).then((response) => {
      const idx = this.created[factory].indexOf(id);
      this.debugSection('Deleted Id', `Id: ${id}`);
      this.created[factory].splice(idx, 1);
    });
  }
}

module.exports = ApiDataFactory;

function createRequestFromFunction(param, data) {
  if (typeof param !== 'function') return;
  return param(data);
}
<commit_msg>Fix typo (#1557)<commit_after>const path = require('path');
const requireg = require('requireg');
const Helper = require('../helper');
const REST = require('./REST');

/**
 * Helper for managing remote data using REST API.
 * Uses data generators like [rosie](https://github.com/rosiejs/rosie) or factory girl to create new record.
 *
 * By defining a factory you set the rules of how data is generated.
 * This data will be saved on server via REST API and deleted in the end of a test.
 *
 * ## Use Case
 *
 * Acceptance tests interact with a websites using UI and real browser.
 * There is no way to create data for a specific test other than from user interface.
 * That makes tests slow and fragile. Instead of testing a single feature you need to follow all creation/removal process.
 *
 * This helper solves this problem.
 * Most of web application have API, and it can be used to create and delete test records.
 * By combining REST API with Factories you can easily create records for tests:
 *
 * ```js
 * I.have('user', { login: 'davert', email: 'davert@mail.com' });
 * let id = await I.have('post', { title: 'My first post'});
 * I.haveMultiple('comment', 3, {post_id: id});
 * ```
 *
 * To make this work you need
 *
 * 1. REST API endpoint which allows to perform create / delete requests and
 * 2. define data generation rules
 *
 * ### Setup
 *
 * Install [Rosie](https://github.com/rosiejs/rosie) and [Faker](https://www.npmjs.com/package/faker) libraries.
 *
 * ```sh
 * npm i rosie faker --save-dev
 * ```
 *
 * Create a factory file for a resource.
 *
 * See the example for Posts factories:
 *
 * ```js
 * // tests/factories/posts.js
 *
 * var Factory = require('rosie').Factory;
 * var faker = require('faker');
 *
 * module.exports = new Factory()
 *    // no need to set id, it will be set by REST API
 *    .attr('author', () => faker.name.findName())
 *    .attr('title', () => faker.lorem.sentence())
 *    .attr('body', () => faker.lorem.paragraph());
 * ```
 * For more options see [rosie documentation](https://github.com/rosiejs/rosie).
 *
 * Then configure ApiDataHelper to match factories and REST API:

 * ### Configuration
 *
 * ApiDataFactory has following config options:
 *
 * * `endpoint`: base URL for the API to send requests to.
 * * `cleanup` (default: true): should inserted records be deleted up after tests
 * * `factories`: list of defined factories
 * * `returnId` (default: false): return id instead of a complete response when creating items.
 * * `headers`: list of headers
 * * `REST`: configuration for REST requests
 *
 * See the example:
 *
 * ```js
 *  ApiDataFactory: {
 *    endpoint: "http://user.com/api",
 *    cleanup: true,
 *    headers: {
 *      'Content-Type': 'application/json',
 *      'Accept': 'application/json',
 *    },
 *    factories: {
 *      post: {
 *        uri: "/posts",
 *        factory: "./factories/post",
 *      },
 *      comment: {
 *        factory: "./factories/comment",
 *        create: { post: "/comments/create" },
 *        delete: { post: "/comments/delete/{id}" },
 *        fetchId: (data) => data.result.id
 *      }
 *    }
 * }
 * ```

 * It is required to set REST API `endpoint` which is the baseURL for all API requests.
 * Factory file is expected to be passed via `factory` option.
 *
 * This Helper uses [REST](http://codecept.io/helpers/REST/) helper and accepts its configuration in "REST" section.
 * For instance, to set timeout you should add:
 *
 * ```js
 * "ApiDataFactory": {
 *    "REST": {
 *      "timeout": "100000",
 *   }
 * }
 * ```
 *
 * ### Requests
 *
 * By default to create a record ApiDataFactory will use endpoint and plural factory name:
 *
 * * create: `POST {endpoint}/{resource} data`
 * * delete: `DELETE {endpoint}/{resource}/id`
 *
 * Example (`endpoint`: `http://app.com/api`):
 *
 * * create: POST request to `http://app.com/api/users`
 * * delete: DELETE request to `http://app.com/api/users/1`
 *
 * This behavior can be configured with following options:
 *
 * * `uri`: set different resource uri. Example: `uri: account` => `http://app.com/api/account`.
 * * `create`: override create options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/create" }`
 * * `delete`: override delete options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/delete/{id}" }`
 *
 * Requests can also be overridden with a function which returns [axois request config](https://github.com/axios/axios#request-config).
 *
 * ```js
 * create: (data) => ({ method: 'post', url: '/posts', data }),
 * delete: (id) => ({ method: 'delete', url: '/posts', data: { id } })
 *
 * ```
 *
 * Requests can be updated on the fly by using `onRequest` function. For instance, you can pass in current session from a cookie.
 *
 * ```js
 *  onRequest: async (request) => {
 *     // using global codeceptjs instance
 *     let cookie = await codeceptjs.container.helpers('WebDriver').grabCookie('session');
 *     request.headers = { Cookie: `session=${cookie.value}` };
 *   }
 * ```
 *
 * ### Responses
 *
 * By default `I.have()` returns a promise with a created data:
 *
 * ```js
 * let client = await I.have('client');
 * ```
 *
 * Ids of created records are collected and used in the end of a test for the cleanup.
 * If you need to receive `id` instead of full response enable `returnId` in a helper config:
 *
 * ```js
 * // returnId: false
 * let clientId = await I.have('client');
 * // clientId == 1
 *
 * // returnId: true
 * let clientId = await I.have('client');
 * // client == { name: 'John', email: 'john@snow.com' }
 * ```
 *
 * By default `id` property of response is taken. This behavior can be changed by setting `fetchId` function in a factory config.
 *
 *
 * ```js
 *    factories: {
 *      post: {
 *        uri: "/posts",
 *        factory: "./factories/post",
 *        fetchId: (data) => data.result.posts[0].id
 *      }
 *    }
 * ```
 *
 *
 * ## Methods
 */
class ApiDataFactory extends Helper {
  constructor(config) {
    super(config);

    const defaultConfig = {
      cleanup: true,
      REST: {},
      factories: {},
      returnId: false,
    };
    this.config = Object.assign(defaultConfig, this.config);

    if (this.config.headers) this.config.REST.defaultHeaders = this.config.headers;
    if (this.config.onRequest) this.config.REST.onRequest = this.config.onRequest;
    this.restHelper = new REST(Object.assign(this.config.REST, { endpoint: this.config.endpoint }));
    this.factories = this.config.factories;

    for (const factory in this.factories) {
      const factoryConfig = this.factories[factory];
      if (!factoryConfig.uri && !factoryConfig.create) {
        throw new Error(`Uri for factory "${factory}" is not defined. Please set "uri" parameter:

    "factories": {
      "${factory}": {
        "uri": ...
`);
      }

      if (!factoryConfig.create) factoryConfig.create = { post: factoryConfig.uri };
      if (!factoryConfig.delete) factoryConfig.delete = { delete: `${factoryConfig.uri}/{id}` };

      this.factories[factory] = factoryConfig;
    }

    this.created = {};
    Object.keys(this.factories).forEach(f => this.created[f] = []);
  }

  static _checkRequirements() {
    try {
      requireg('axios');
      requireg('rosie');
    } catch (e) {
      return ['axios', 'rosie'];
    }
  }

  _after() {
    if (!this.config.cleanup) {
      return Promise.resolve();
    }
    const promises = [];

    // clean up all created items
    for (const factoryName in this.created) {
      const createdItems = this.created[factoryName];
      if (!createdItems.length) continue;
      this.debug(`Deleting ${createdItems.length} ${factoryName}(s)`);
      for (const id in createdItems) {
        promises.push(this._requestDelete(factoryName, createdItems[id]));
      }
    }

    return Promise.all(promises);
  }

  /**
   * Generates a new record using factory and saves API request to store it.
   *
   * ```js
   * // create a user
   * I.have('user');
   * // create user with defined email
   * // and receive it when inside async function
   * const user = await I.have('user', { email: 'user@user.com'});
   * ```
   *
   * @param {*} factory factory to use
   * @param {*} params predefined parameters
   */
  have(factory, params) {
    const item = this._createItem(factory, params);
    this.debug(`Creating ${factory} ${JSON.stringify(item)}`);
    return this._requestCreate(factory, item);
  }

  /**
   * Generates bunch of records and saves multiple API requests to store them.
   *
   * ```js
   * // create 3 posts
   * I.haveMultiple('post', 3);
   *
   * // create 3 posts by one author
   * I.haveMultiple('post', 3, { author: 'davert' });
   * ```
   *
   * @param {*} factory
   * @param {*} times
   * @param {*} params
   */
  haveMultiple(factory, times, params) {
    const promises = [];
    for (let i = 0; i < times; i++) {
      promises.push(this.have(factory, params));
    }
    return Promise.all(promises);
  }

  _createItem(model, data) {
    if (!this.factories[model]) {
      throw new Error(`Factory ${model} is not defined in config`);
    }
    let modulePath = this.factories[model].factory;
    try {
      try {
        require.resolve(modulePath);
      } catch (e) {
        modulePath = path.join(global.codecept_dir, modulePath);
      }
      const builder = require(modulePath);
      return builder.build(data);
    } catch (err) {
      throw new Error(`Couldn't load factory file from ${modulePath}, check that

  "factories": {
    "${model}": {
      "factory": "./path/to/factory"

points to valid factory file.
Factory file should export an object with build method.

Current file error: ${err.message}`);
    }
  }

  _fetchId(body, factory) {
    if (this.config.factories[factory].fetchId) {
      return this.config.factories[factory].fetchId(body);
    }
    if (body.id) return body.id;
    if (body[factory] && body[factory].id) return body[factory].id;
    return null;
  }

  /**
   * Executes request to create a record in API.
   * Can be replaced from a in custom helper.
   *
   * @param {*} factory
   * @param {*} data
   */
  _requestCreate(factory, data) {
    let request = createRequestFromFunction(this.factories[factory].create, data);

    if (!request) {
      const method = Object.keys(this.factories[factory].create)[0];
      const url = this.factories[factory].create[method];
      request = {
        method,
        url,
        data,
      };
    }

    request.baseURL = this.config.endpoint;

    return this.restHelper._executeRequest(request).then((resp) => {
      const id = this._fetchId(resp.data, factory);
      this.created[factory].push(id);
      this.debugSection('Created', `Id: ${id}`);
      if (this.config.returnId) return id;
      return resp.data;
    });
  }

  /**
   * Executes request to delete a record in API
   * Can be replaced from a custom helper.
   *
   * @param {*} factory
   * @param {*} id
   */
  _requestDelete(factory, id) {
    if (!this.factories[factory].delete) return;
    let request = createRequestFromFunction(this.factories[factory].delete, id);

    if (!request) {
      const method = Object.keys(this.factories[factory].delete)[0];
      const url = this.factories[factory].delete[method].replace('{id}', id);
      request = {
        method,
        url,
      };
    }

    request.baseURL = this.config.endpoint;

    return this.restHelper._executeRequest(request).then((response) => {
      const idx = this.created[factory].indexOf(id);
      this.debugSection('Deleted Id', `Id: ${id}`);
      this.created[factory].splice(idx, 1);
    });
  }
}

module.exports = ApiDataFactory;

function createRequestFromFunction(param, data) {
  if (typeof param !== 'function') return;
  return param(data);
}
<|endoftext|>


<commit_before>const resources = require('../resources');

module.exports = function(bindClass) {
  for(var prop in resources) {
    bindClass[prop] = resources[prop];
  }
};<commit_msg>prefer arrows<commit_after>const resources = require('../resources');

module.exports = (bindClass) => {
  for(var prop in resources) {
    bindClass[prop] = resources[prop];
  }
};<|endoftext|>


<commit_before>var axios = require('axios');
var createError = require('../error');

// Performs an https request
//
// o.path - the url path
// o.method - GET, PUT, POST, DELETE
// o.data - form data
// o.decodeResponse - function that decodes a successful response
// o.contentType - override content type
module.exports = function(o) {
  function handleResponse(data) {
    if (o.decodeResponse) {
      return o.decodeResponse(data);
    } else {
      return data.data || data;
    }
  }

  function handleError(err) {
    throw createError(err.status, err.responseText);
  }

  var url = 'https://' + this.truevault.getOption('host') + o.path;
  var headers = {
    'Authorization' : this.truevault.getOption('auth')
  };

  if (o.contentType) {
    headers['Content-Type'] = o.contentType;
  }

  var transformResponse = axios.defaults.transformResponse.concat([handleResponse]);

  var options = {
    url: url,
    method: o.method,
    headers: headers,
    data: o.data,
    transformResponse: transformResponse
  };

  return axios(options);
};
<commit_msg>continue handling errors the same way we used to<commit_after>var axios = require('axios');
var createError = require('../error');

// Performs an https request
//
// o.path - the url path
// o.method - GET, PUT, POST, DELETE
// o.data - form data
// o.decodeResponse - function that decodes a successful response
// o.contentType - override content type
module.exports = function(o) {
  function handleResponse(data) {
    if (o.decodeResponse) {
      return o.decodeResponse(data);
    } else {
      return data.data || data;
    }
  }

  function handleError(response) {
    throw createError(response.status, JSON.stringify(response.data));
  }

  var url = 'https://' + this.truevault.getOption('host') + o.path;
  var headers = {
    'Authorization' : this.truevault.getOption('auth')
  };

  if (o.contentType) {
    headers['Content-Type'] = o.contentType;
  }

  var transformResponse = axios.defaults.transformResponse.concat([handleResponse]);

  var options = {
    url: url,
    method: o.method,
    headers: headers,
    data: o.data,
    transformResponse: transformResponse
  };

  return axios(options).catch(handleError);
};
<|endoftext|>


<commit_before>// Note: You must restart bin/webpack-watcher for changes to take effect

var path = require('path')
var process = require('process')
var glob = require('glob')
var extname = require('path-complete-extname')
var distPath = process.env.WEBPACK_DIST_DIR

if(distPath === undefined) {
  distPath = 'packs'
}

config = {
  entry: glob.sync(path.join('..', 'app', 'javascript', 'packs', '*.js*')).reduce(
    function(map, entry) {
      var basename = path.basename(entry, extname(entry))
      map[basename] = entry
      return map
    }, {}
  ),

  output: { filename: '[name].js', path: path.resolve('..', 'public', distPath) },

  module: {
    rules: [
      { test: /\.coffee(.erb)?$/, loader: "coffee-loader" },
      {
        test: /\.js(.erb)?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [ 'latest', { 'es2015': { 'modules': false } } ]
          ]
        }
      },
      {
        test: /.erb$/,
        enforce: 'pre',
        exclude: /node_modules/,
        loader: 'rails-erb-loader',
        options: {
          runner: 'DISABLE_SPRING=1 ../bin/rails runner'
        }
      },
    ]
  },

  plugins: [],

  resolve: {
    extensions: [ '.js', '.coffee' ],
    modules: [
      path.resolve('../app/javascript'),
      path.resolve('../vendor/node_modules')
    ]
  },

  resolveLoader: {
    modules: [ path.resolve('../vendor/node_modules') ]
  }
}

module.exports = {
  distPath: distPath,
  config: config
}
<commit_msg>Change variable name to distDir<commit_after>// Note: You must restart bin/webpack-watcher for changes to take effect

var path = require('path')
var process = require('process')
var glob = require('glob')
var extname = require('path-complete-extname')
var distDir = process.env.WEBPACK_DIST_DIR

if(distDir === undefined) {
  distDir = 'packs'
}

config = {
  entry: glob.sync(path.join('..', 'app', 'javascript', 'packs', '*.js*')).reduce(
    function(map, entry) {
      var basename = path.basename(entry, extname(entry))
      map[basename] = entry
      return map
    }, {}
  ),

  output: { filename: '[name].js', path: path.resolve('..', 'public', distDir) },

  module: {
    rules: [
      { test: /\.coffee(.erb)?$/, loader: "coffee-loader" },
      {
        test: /\.js(.erb)?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [ 'latest', { 'es2015': { 'modules': false } } ]
          ]
        }
      },
      {
        test: /.erb$/,
        enforce: 'pre',
        exclude: /node_modules/,
        loader: 'rails-erb-loader',
        options: {
          runner: 'DISABLE_SPRING=1 ../bin/rails runner'
        }
      },
    ]
  },

  plugins: [],

  resolve: {
    extensions: [ '.js', '.coffee' ],
    modules: [
      path.resolve('../app/javascript'),
      path.resolve('../vendor/node_modules')
    ]
  },

  resolveLoader: {
    modules: [ path.resolve('../vendor/node_modules') ]
  }
}

module.exports = {
  distDir: distDir,
  config: config
}
<|endoftext|>


<commit_before>// Cool Helpers
import * as dom from '../helpers/dom';
import * as classy from '../helpers/classy';
import * as event from '../helpers/event';

// ┌───────────┐
// │ Third Nav │
// └───────────┘
// sticks things to the window

function thirdNav () {
  var nav = dom.findElements('.js-nav-overflow')[0];
  var leftBtn = dom.findElements('.js-overflow-left')[0];
  var rightBtn = dom.findElements('.js-overflow-right')[0];

  function scroll (distance) {
    nav.scrollLeft += distance;
  }

  function resize () {
    classy.remove(leftBtn, 'is-active');
    classy.remove(rightBtn, 'is-active');
    if (nav.scrollLeft > 0) classy.add(leftBtn, 'is-active');
    if (nav.scrollLeft + nav.clientWidth + 5 < nav.scrollWidth) classy.add(rightBtn, 'is-active');
  }

  if (nav) {
    console.log(nav);
    if (leftBtn) {
      event.add(leftBtn, event.click(), scroll.bind(null, -40));
    }
    if (rightBtn) {
      event.add(rightBtn, event.click(), scroll.bind(null, 40));
    }
    event.add(nav, 'scroll', resize);
    event.add(window, 'resize', resize);
    resize();
  }
}

export default thirdNav;
<commit_msg>remove console<commit_after>// Cool Helpers
import * as dom from '../helpers/dom';
import * as classy from '../helpers/classy';
import * as event from '../helpers/event';

// ┌───────────┐
// │ Third Nav │
// └───────────┘
// sticks things to the window

function thirdNav () {
  var nav = dom.findElements('.js-nav-overflow')[0];
  var leftBtn = dom.findElements('.js-overflow-left')[0];
  var rightBtn = dom.findElements('.js-overflow-right')[0];

  function scroll (distance) {
    nav.scrollLeft += distance;
  }

  function resize () {
    classy.remove(leftBtn, 'is-active');
    classy.remove(rightBtn, 'is-active');
    if (nav.scrollLeft > 0) classy.add(leftBtn, 'is-active');
    if (nav.scrollLeft + nav.clientWidth + 5 < nav.scrollWidth) classy.add(rightBtn, 'is-active');
  }

  if (nav) {
    if (leftBtn) {
      event.add(leftBtn, event.click(), scroll.bind(null, -40));
    }
    if (rightBtn) {
      event.add(rightBtn, event.click(), scroll.bind(null, 40));
    }
    event.add(nav, 'scroll', resize);
    event.add(window, 'resize', resize);
    resize();
  }
}

export default thirdNav;
<|endoftext|>


<commit_before>import { isArray } from "../utils/array"
/**
 * Issue #569: collapse::toggle::state triggered too many times
 * @link https://github.com/bootstrap-vue/bootstrap-vue/issues/569
 */
export default {
    methods: {
        /**
         * Safely register event listeners on the root Vue node.
         * While Vue automatically removes listeners for individual components,
         * when a component registers a listener on root and is destroyed,
         * this orphans a callback because the node is gone,
         * but the root does not clear the callback.
         *
         * This adds a non-reactive prop to a vm on the fly
         * in order to avoid object observation and its performance costs
         * to something that needs no reactivity.
         * It should be highly unlikely there are any naming collisions.
         * @param {string} event
         * @param {function} callback
         * @chainable
         */
        listenOnRoot(event, callback) {
            if (!this.__bv_root_listeners || !isArray(this.__bv_root_listeners)) {
                this.__bv_root_listeners = []
            }
            this.__bv_root_listeners.push({ event, callback })
            this.$root.$on(event, callback)

            return this
        },

        /**
         * Convenience method for calling vm.$emit on vm.$root.
         * @param {string} event
         * @param {*} args
         * @chainable
         */
        emitOnRoot(event, ...args) {
            this.$root.$emit(event, ...args)

            return this
        }
    },

    destroyed() {
        if (this.__bv_root_listeners && isArray(this.__bv_root_listeners)) {
            while (this.__bv_root_listeners.length > 0) {
                // shift to process in order
                const { event, callback } = this.__bv_root_listeners.shift()
                this.$root.$off(event, callback)
            }
        }
    }
}
<commit_msg>feat(listenonroot): Use a constant for private property name (#700)<commit_after>import { isArray } from "../utils/array"
/**
 * Issue #569: collapse::toggle::state triggered too many times
 * @link https://github.com/bootstrap-vue/bootstrap-vue/issues/569
 */

const BVRL = '__BV_root_listeners__';

export default {
    methods: {
        /**
         * Safely register event listeners on the root Vue node.
         * While Vue automatically removes listeners for individual components,
         * when a component registers a listener on root and is destroyed,
         * this orphans a callback because the node is gone,
         * but the root does not clear the callback.
         *
         * This adds a non-reactive prop to a vm on the fly
         * in order to avoid object observation and its performance costs
         * to something that needs no reactivity.
         * It should be highly unlikely there are any naming collisions.
         * @param {string} event
         * @param {function} callback
         * @chainable
         */
        listenOnRoot(event, callback) {
            if (!this[BVRL] || !isArray(this[BVRL])) {
                this[BVRL] = [];
            }
            this[BVRL].push({ event, callback });
            this.$root.$on(event, callback);
            return this;
        },

        /**
         * Convenience method for calling vm.$emit on vm.$root.
         * @param {string} event
         * @param {*} args
         * @chainable
         */
        emitOnRoot(event, ...args) {
            this.$root.$emit(event, ...args);
            return this;
        }
    },

    destroyed() {
        if (this[BVRL] && isArray(this[BVRL])) {
            while (this[BVRL].length > 0) {
                // shift to process in order
                const { event, callback } = this[BVRL].shift();
                this.$root.$off(event, callback);
            }
        }
    }
}
<|endoftext|>


<commit_before>import path from 'path';
import debug from 'debug';
import sh from 'shelljs';
import * as meteor from '../meteor/';
import * as mongo from '../mongo/';
import * as docker from '../docker/';
const log = debug('mup:module:default');

sh.config.silent = true;

export function deploy(api) {
  log('exec => mup deploy');
  return meteor.deploy(api);
}

export function help(api) {
  log('exec => mup help');
}
export function init(/* api */) {
  log('exec => mup init');

  // TODO check if mup.js or settings.json files exists
  const src = path.resolve(__dirname, 'template/*');
  const dst = process.cwd();
  sh.cp(src, dst);
}

export function logs(api) {
  log('exec => mup logs');
  return meteor.logs(api);
}

export function reconfig(api) {
  log('exec => mup reconfig');
  return meteor.envconfig(api)
    .then(() => meteor.start(api));
}

export function restart(api) {
  log('exec => mup restart');
  return meteor.stop(api)
    .then(() => meteor.start(api));
}

export function setup(api) {
  log('exec => mup setup');
  const config = api.getConfig();
  return docker.setup(api)
    .then(() => {
      if (config.mongo) {
        return Promise.all([
          meteor.setup(api),
          mongo.setup(api)
        ]);
      }
      return meteor.setup(api);
    })
    .then(() => {
      if (config.mongo) {
        return mongo.start(api);
      }
    });
}

export function start(api) {
  log('exec => mup start');
  return meteor.start(api);
}

export function stop(api) {
  log('exec => mup stop');
  return meteor.stop(api);
}
<commit_msg>Refactored code<commit_after>import path from 'path';
import debug from 'debug';
import sh from 'shelljs';
import * as meteor from '../meteor/';
import * as mongo from '../mongo/';
import * as docker from '../docker/';
const log = debug('mup:module:default');

sh.config.silent = true;

export function deploy(api) {
  log('exec => mup deploy');
  return meteor.deploy(api);
}

export function help(api) {
  log('exec => mup help');
}
export function init(/* api */) {
  log('exec => mup init');

  // TODO check if mup.js or settings.json files exists
  const src = path.resolve(__dirname, 'template/*');
  const dst = process.cwd();
  sh.cp(src, dst);
}

export function logs(api) {
  log('exec => mup logs');
  return meteor.logs(api);
}

export function reconfig(api) {
  log('exec => mup reconfig');
  return meteor.envconfig(api)
    .then(() => meteor.start(api));
}

export function restart(api) {
  log('exec => mup restart');
  return meteor.stop(api)
    .then(() => meteor.start(api));
}

export function setup(api) {
  log('exec => mup setup');
  const config = api.getConfig();
  return docker.setup(api)
    .then(() => {
      if (config.mongo) {
        return Promise.all([
          meteor.setup(api),
          mongo.setup(api)
        ])
        .then(() => {
          return mongo.start(api);
        });
      }
      return meteor.setup(api);
    });
}

export function start(api) {
  log('exec => mup start');
  return meteor.start(api);
}

export function stop(api) {
  log('exec => mup stop');
  return meteor.stop(api);
}
<|endoftext|>


<commit_before>'use strict'
module.exports = function (parsed) {
  if (parsed instanceof Object) {
    console.log('??', arguments)
    let cache = this._cache
    if (!cache) {
      this.setKey('_cache', {
        // TODO this messes things up, think of better way to do this
        // properties: this.properties,
        ChildConstructor: this.ChildConstructor,
        val: this
      })

      cache = this._cache
    }
    return cache
  }
}
<commit_msg>removed logs<commit_after>'use strict'
module.exports = function (parsed) {
  if (parsed instanceof Object) {
    let cache = this._cache
    if (!cache) {
      this.setKey('_cache', {
        // TODO this messes things up, think of better way to do this
        // properties: this.properties,
        ChildConstructor: this.ChildConstructor,
        val: this
      })

      cache = this._cache
    }
    return cache
  }
}
<|endoftext|>


<commit_before>var utils = require('../utils'),
	Template = require('../template'),
	Context = require('../context'),
	stringEngine = require('./string'),
	SOD = stringEngine.SOD,
	View = require('../view');

var firstMethods = {
	//_________________________________ local context management
	context: function(context, args) {
		var data = args[0],
			parent = args[1] || context,
			path = args[2],
			ctx = new Context(data, parent, path);
		(context.children = context.children || []).push(ctx);
		return ctx;
	},
	// ____________________________________ WITH
	with: function(context, descriptor, args) {
		var path = args[0],
			// produce local context and store it in parent
			ctx = new Context(typeof path === 'string' ? context.get(path) : path, context, path);
		(context.children = context.children || []).push(ctx);
	},
	//________________________________ Conditonal node rendering
	if: function(context, args) {
		var ok, condition = args[0],
			successTempl = args[1],
			failTempl = args[2];
		var exec = function(ok, type, path) {
			if (ok)
				firstPass(successTempl, context);
			else if (failTempl)
				firstPass(failTempl, context);
		};
		if (condition && condition.__interpolable__) {
			ok = condition.output(context);
			condition.subscribeTo(context, exec);
		} else if (typeof condition === 'function')
			ok = condition.call(this, context);
		exec(ok, 'set');
	},
	//_________________________________________ EACH
	each: function(context, args) {
		var path = args[0],
			template = args[1],
			emptyTemplate = args[2],
			emptyInitialised = false,
			data = path,
			contexts = [];

		var updateArray = function(value, type, path, index) {
			// on array update : produce or maintain associated local contexts array
			var ctx, ctxs = contexts;
			switch (type) {
				case 'reset':
				case 'set':

					if (!value.length && !emptyInitialised)
						firstPass(emptyTemplate, context); // traverse empty template with firstPass

					var j = 0;
					for (var len = value.length; j < len; ++j) // reset existing or create new ctx
					{
						if (ctxs[j]) // reset existing
							ctxs[j].reset(value[j]);
						else { // create new ctx
							ctx = new Context(value[j], context);
							ctxs.push(ctx);
							firstPass(template, ctx); // traverse child template with firstPass
						}
					}
					if (j < ctxs.length) // remove additional ctx that is not used any more
					{
						for (var i = j, len = ctxs.length; i < len; ++i)
							ctxs[i].destroy();
						ctxs.splice(j);
					}
					break;
				case 'removeAt':
					ctxs.splice(index, 1);
					break;
				case 'push':
					ctx = new Context(value, context)
					ctxs.push(ctx);
					firstPass(template, ctx);
					break;
			}
		};

		if (typeof path === 'string') {
			context.subscribe(path, updateArray);
			context.subscribe(path + '.*', function(value, type, path, key) {
				// on array's item update
				var ctx = contexts[key];
				if (ctx)
					return ctx.reset(value); // update associated context
			});
			data = context.get(path);
		}
		if (data)
			updateArray(data, 'set');
		// store local contexts array in parent
		(context.children = context.children || []).push(contexts);
	},
	//________________________________ TAGS
	tag: function(context, args) {
		var name = args[0],
			template = args[1];
		if (template)
			firstPass(template, context); // traverse tag template with frstPass
	},
	//_________________________________ CLIENT/SERVER
	client: function(context, args) {
		if (context.env.data.isServer)
			return;
		firstPass(args[0], context); // traverse client template with frstPass
	},
	server: function(context, args) {
		if (!context.env.data.isServer)
			return;
		firstPass(args[0], context); // traverse server template with frstPass
	}
};

var secondMethods = {
	//_________________________________ local context management
	context: function(context, descriptor) {
		if (context.children) // catch context produced in firstPass
			descriptor.context = context.children.shift();
	},
	// ____________________________________ WITH
	with: function(context, descriptor, args) {
		if (!context.children)
			return;
		var path = args[0],
			template = args[1],
			ctx = context.children.shift(); // catch context produced in firstPass
		secondPass(template, ctx, descriptor);
	},
	//_________________________________________ EACH
	each: function(context, descriptor, args) {
		if (!context.children)
			return;
		var contexts = context.children.shift(); // catch contexts array produced in firstPass
		if (contexts && contexts.length) {
			var template = args[1],
				emptyTemplate = args[2];
			if (!contexts.length)
				secondPass(emptyTemplate, context, descriptor);
			else
				for (var i = 0, len = contexts.length; i < len; ++i)
					secondPass(template, contexts[i], descriptor);
		}
	},
	//________________________________ TAGS
	tag: function(context, descriptor, args) {
		var name = args[0],
			template = args[1],
			newDescriptor = new SOD();
		if (template)
			secondPass(template, context, newDescriptor);
		utils.tagOutput(descriptor, newDescriptor, name);
	},
	//________________________________ Conditonal node rendering
	if: function(context, descriptor, args) {
		var ok, condition = args[0],
			successTempl = args[1],
			failTempl = args[2];
		if (condition && condition.__interpolable__)
			ok = condition.output(context);
		else if (type === 'function')
			ok = condition.call(this, context);
		if (ok)
			secondPass(successTempl, context, descriptor);
		else if (failTempl)
			secondPass(failTempl, context, descriptor);
	},
	//_________________________________ CLIENT/SERVER
	client: function(context, descriptor, args) {
		if (context.env.data.isServer)
			return;
		secondPass(args[0], context, descriptor);
	},
	server: function(context, descriptor, args) {
		if (!context.env.data.isServer)
			return;
		secondPass(args[0], context, descriptor);
	},
	contentSwitch: null,
	cssSwitch: null
};

function secondPass(template, context, descriptor) {
	// apply string rendering only
	descriptor = descriptor ||  new SOD();
	var handler,
		f;
	for (var i = 0, len = template._queue.length; i < len; ++i) {
		handler = template._queue[i];
		if (handler.engineBlock)
			f = handler.engineBlock.twopass.second || handler.engineBlock.string;
		else if (handler.func) {
			if (handler.firstPass)
				continue;
			else
				f = handler.func;
		} else if (secondMethods[handler.name])
			f = secondMethods[handler.name];
		else
			f = stringEngine[handler.name];

		f(descriptor.context || context, descriptor, handler.args);
	}
}

function firstPass(template, context) {
	// apply contexts construction only
	var handler,
		f,
		newContext,
		ctx;
	for (var i = 0, len = template._queue.length; i < len; ++i) {
		handler = template._queue[i];
		if (handler.engineBlock) {
			f = handler.engineBlock.twopass.first;
			if (!f)
				continue;
		} else if (handler.func) {
			if (!handler.firstPass)
				continue;
			else
				f = handler.func;
		} else if (!firstMethods[handler.name])
			continue;
		else
			f = firstMethods[handler.name];
		ctx = f(newContext || context, handler.args);
		if (ctx && ctx.__yContext__)
			newContext = ctx;
	}
}

Template.prototype.twopass = View.prototype.twopass = function(context) {
	context = context || new Context();
	firstPass(this, context); // apply first pass : construct contexts
	var self = this;
	// wait for context stabilisation
	return context.stabilised().then(function(context) {
		// then apply second pass : render to string
		var descriptor = new SOD();
		secondPass(self, context, descriptor);
		return descriptor.children;
	});
};

module.exports = {
	firstMethods: firstMethods,
	secondMethods: secondMethods,
	firstPass: firstPass,
	secondPass: secondPass
};
<commit_msg>adapt twopass to exec refactoring and finalise switch<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 */
var utils = require('../utils'),
	Template = require('../template'),
	Context = require('../context'),
	stringEngine = require('./string'),
	SOD = stringEngine.SOD,
	View = require('../view');

var firstMethods = {
	//_________________________________ local context management
	newContext: function(context, args) {
		var data = args[0],
			parent = args[1] || context,
			path = args[2],
			ctx = new Context(data, parent, path);
		(context.firstPassObjects = context.firstPassObjects || []).push(ctx);
		return ctx;
	},
	// ____________________________________ WITH
	with: function(context, descriptor, args) {
		var path = args[0],
			// produce local context and store it in parent
			ctx = new Context(typeof path === 'string' ? context.get(path) : path, context, path);
		(context.firstPassObjects = context.firstPassObjects || []).push(ctx);
	},
	//________________________________ Conditonal node rendering
	if: function(context, args) {
		var ok, condition = args[0],
			successTempl = args[1],
			failTempl = args[2];
		var exec = function(ok, type, path) {
			if (ok)
				firstPass(successTempl, context);
			else if (failTempl)
				firstPass(failTempl, context);
		};
		if (condition && condition.__interpolable__) {
			ok = condition.output(context);
			condition.subscribeTo(context, exec);
		} else if (typeof condition === 'function')
			ok = condition.call(this, context);
		exec(ok, 'set');
	},
	switch: function(context, args) {
		var expression = args[0],
			dico = args[1],
			seen = {};

		function switchTempl(value) {
			var templ = dico[value] || dico['default'];
			if (templ && !seen[value]) {
				seen[value] = true;
				firstPass(templ, context);
			}
		}
		if (expression.__interpolable__) {
			expression.subscribeTo(context, function(value) {
				switchTempl(value);
			});
			switchTempl(expression.output(context));
		} else if (typeof expression === 'function')
			switchTempl(expression(context));
		else
			switchTempl(expression);
	},
	//_________________________________________ EACH
	each: function(context, args) {
		var path = args[0],
			template = args[1],
			emptyTemplate = args[2],
			emptyInitialised = false,
			data = path,
			contexts = [];

		var updateArray = function(value, type, path, index) {
			// on array update : produce or maintain associated local contexts array
			var ctx, ctxs = contexts;
			switch (type) {
				case 'reset':
				case 'set':

					if (!value.length && !emptyInitialised)
						firstPass(emptyTemplate, context); // traverse empty template with firstPass

					var j = 0;
					for (var len = value.length; j < len; ++j) // reset existing or create new ctx
					{
						if (ctxs[j]) // reset existing
							ctxs[j].reset(value[j]);
						else { // create new ctx
							ctx = new Context(value[j], context);
							ctxs.push(ctx);
							firstPass(template, ctx); // traverse child template with firstPass
						}
					}
					if (j < ctxs.length) // remove additional ctx that is not used any more
					{
						for (var i = j, len = ctxs.length; i < len; ++i)
							ctxs[i].destroy();
						ctxs.splice(j);
					}
					break;
				case 'removeAt':
					ctxs[index].destroy();
					ctxs.splice(index, 1);
					break;
				case 'push':
					ctx = new Context(value, context)
					ctxs.push(ctx);
					firstPass(template, ctx);
					break;
			}
		};

		if (typeof path === 'string') {
			context.subscribe(path, updateArray);
			context.subscribe(path + '.*', function(value, type, path, key) {
				// on array's item update
				var ctx = contexts[key];
				if (ctx)
					return ctx.reset(value); // update associated context
			});
			data = context.get(path);
		}
		if (data)
			updateArray(data, 'set');
		// store local contexts array in parent
		(context.firstPassObjects = context.firstPassObjects || []).push(contexts);
	},
	//________________________________ TAGS
	tag: function(context, args) {
		var name = args[0],
			template = args[1];
		if (template)
			firstPass(template, context); // traverse tag template with frstPass
	},
	//_________________________________ CLIENT/SERVER
	client: function(context, args) {
		if (context.env.data.isServer)
			return;
		firstPass(args[0], context); // traverse client template with frstPass
	},
	server: function(context, args) {
		if (!context.env.data.isServer)
			return;
		firstPass(args[0], context); // traverse server template with frstPass
	}
};

var secondMethods = {
	//_________________________________ local context management
	newContext: function(context, descriptor) {
		if (context.firstPassObjects) // catch context produced in firstPass
			descriptor.context = context.firstPassObjects.shift();
	},
	// ____________________________________ WITH
	with: function(context, descriptor, args) {
		if (!context.firstPassObjects)
			return;
		var path = args[0],
			template = args[1],
			ctx = context.firstPassObjects.shift(); // catch context produced in firstPass
		secondPass(template, ctx, descriptor);
	},
	//_________________________________________ EACH
	each: function(context, descriptor, args) {
		if (!context.firstPassObjects)
			return;
		var contexts = context.firstPassObjects.shift(); // catch contexts array produced in firstPass
		if (contexts && contexts.length) {
			var template = args[1],
				emptyTemplate = args[2];
			if (!contexts.length)
				secondPass(emptyTemplate, context, descriptor);
			else
				for (var i = 0, len = contexts.length; i < len; ++i)
					secondPass(template, contexts[i], descriptor);
		}
	},
	switch: function(context, descriptor, args) {
		var expression = args[0],
			dico = args[1],
			value;
		if (expression.__interpolable__)
			value = expression.output(context);
		else if (typeof expression === 'function')
			value = expression(context);
		else
			value = expression;
		templ = dico[value] || dico['default'];
		if (templ) {
			var sod = new SOD();
			templ.toHTMLString(context, sod);
			if (sod.firstPassObjects)
				descriptor.firstPassObjects += sod.firstPassObjects;
		}
	},
	//________________________________ TAGS
	tag: function(context, descriptor, args) {
		var name = args[0],
			template = args[1],
			newDescriptor = new SOD();
		if (template)
			secondPass(template, context, newDescriptor);
		utils.tagOutput(descriptor, newDescriptor, name);
	},
	//________________________________ Conditonal node rendering
	if: function(context, descriptor, args) {
		var ok, condition = args[0],
			successTempl = args[1],
			failTempl = args[2];
		if (condition && condition.__interpolable__)
			ok = condition.output(context);
		else if (type === 'function')
			ok = condition.call(this, context);
		if (ok)
			secondPass(successTempl, context, descriptor);
		else if (failTempl)
			secondPass(failTempl, context, descriptor);
	},
	//_________________________________ CLIENT/SERVER
	client: function(context, descriptor, args) {
		if (context.env.data.isServer)
			return;
		secondPass(args[0], context, descriptor);
	},
	server: function(context, descriptor, args) {
		if (!context.env.data.isServer)
			return;
		secondPass(args[0], context, descriptor);
	}
};

function secondPass(queue, context, descriptor) {
	descriptor = descriptor ||  new SOD();
	var handler = queue[0],
		index = 0,
		f;
	while (handler) {
		f = null;
		switch (handler.type) {
			case '*':
				f = secondMethods[handler.handler] || stringEngine[handler.handler];
				break;
			case 'string':
			case 'secondPass':
				f = handler.handler;
				break;
			case 'custom':
				f = handler.handler.secondPass;
				break;
		}
		if (!f) {
			handler = this._queue[++index];
			continue;
		}
		f(descriptor.context || context, descriptor, handler.args);
		if (handler.suspendAfter)
			break;
		handler = queue[++index];
	}
}

function firstPass(queue, context) {
	var handler = queue[0],
		newContext,
		index = 0,
		ctx,
		f;
	while (handler) {
		f = null;
		switch (handler.type) {
			case '*':
				f = firstMethods[handler.handler];
				break;
			case 'context':
			case 'firstPass':
				f = handler.handler;
				break;
			case 'custom':
				f = handler.handler.firstPass;
				break;
		}
		if (!f) {
			handler = this._queue[++index];
			continue;
		}
		ctx = f(newContext || context, handler.args);
		if (ctx && ctx.__yContext__)
			newContext = ctx;
		if (handler.suspendAfter)
			break;
		handler = queue[++index];
	}
}

Template.prototype.twopass = View.prototype.twopass = function(context) {
	context = context || new Context();
	firstPass(this._queue, context); // apply first pass : construct contexts
	var self = this;
	// wait for context stabilisation
	return context.stabilised().then(function(context) {
		// then apply second pass : render to string
		var descriptor = new SOD();
		secondPass(self._queue, context, descriptor);
		return descriptor.firstPassObjects;
	});
};

module.exports = {
	firstMethods: firstMethods,
	secondMethods: secondMethods,
	firstPass: firstPass,
	secondPass: secondPass
};
<|endoftext|>


<commit_before>var fs     = require("fs"),
    path   = require("path"),
    mkdirp = require('mkdirp');

const lattenFile = (src, dest, {r, g, b}) => {
    return new Promise((resolve, reject) => {
        fs.createReadStream(src)
        .pipe(new PNG({
            colorType: 2,
            bgColor: {
                red: r,
                green: g,
                blue: b
            }
        }))
        .on('parsed', function() {
            var writeStream = fs.createWriteStream(dest).on('finish', resolve);
            this.pack().pipe(writeStream);
        });
    })
}

const writeFile = (src, dest) => {
    return new Promise((resolve, reject) => {
    	// 加后会报错
        // mkdirp(path.dirname(dest), function (err) {
        //     if (err) console.error(err)
            let writeStream = fs.createWriteStream(dest).on('finish', resolve);
            fs.createReadStream(src).pipe(writeStream);
        // });
    })
}

class ImageFlatten {
    constructor(bgColor) {
        // super(...args);
        this.apply = this.apply.bind(this)
    }

    apply(data, callback) {

        let tmpDir = path.join(global.tmpDir, 'flatten', data.ratio);
        let {
            frames,
            animations
        } = data;

        mkdirp(tmpDir, function (err) {

            let pr = [];

            for (let key in animations) {
                let p = animations[key].map((fIndex, i) => {

                    let src = frames[fIndex]['src'],
                    	filepath = path.join(tmpDir, path.basename(src));

                    	// 将名字进行修改成 folder_filename
                    	// filepath = path.join(tmpDir, (key !== '' ? key + '_' + path.basename(src) : path.basename(src)));

                    frames[fIndex]['src'] = filepath;
                    return writeFile(src, filepath)
                })
                pr.concat(p)
            }
			Promise.all(pr).then(() => {
				console.log(' ✔ flatten done');
				callback && callback(data);
			});
        })
    }
}

module.exports = ImageFlatten;
<commit_msg>[bugfix] concat<commit_after>var fs     = require("fs"),
    path   = require("path"),
    mkdirp = require('mkdirp');

const lattenFile = (src, dest, {r, g, b}) => {
    return new Promise((resolve, reject) => {
        fs.createReadStream(src)
        .pipe(new PNG({
            colorType: 2,
            bgColor: {
                red: r,
                green: g,
                blue: b
            }
        }))
        .on('parsed', function() {
            var writeStream = fs.createWriteStream(dest).on('finish', resolve);
            this.pack().pipe(writeStream);
        });
    })
}

const writeFile = (src, dest) => {
    return new Promise((resolve, reject) => {
        let writeStream = fs.createWriteStream(dest).on('finish', resolve);
        fs.createReadStream(src).pipe(writeStream);
    })
}

class ImageFlatten {
    constructor(bgColor) {
        // super(...args);
        this.apply = this.apply.bind(this)
    }

    apply(data, callback) {

        let tmpDir = path.join(global.tmpDir, 'flatten', data.ratio);
        let {
            frames,
            animations
        } = data;

        mkdirp(tmpDir, function (err) {

            let pr = [];

            for (let key in animations) {
                let p = animations[key].map((fIndex, i) => {

                    let src = frames[fIndex]['src'],
                    	filepath = path.join(tmpDir, path.basename(src));

                    	// 将名字进行修改成 folder_filename
                    	// filepath = path.join(tmpDir, (key !== '' ? key + '_' + path.basename(src) : path.basename(src)));

                    frames[fIndex]['src'] = filepath;
                    return writeFile(src, filepath)
                })
                pr = pr.concat(...p)
            }
			Promise.all(pr).then(() => {
				console.log(' ✔ flatten done');
				callback && callback(data);
			});
        })
    }
}

module.exports = ImageFlatten;
<|endoftext|>


<commit_before>'use strict';

const flatten = require('../../support/flattenMessage');
const merge = require('lodash.merge');

const defaultConfig = {
  list: false,
  filterList: false
};

module.exports = {
  open(context, options) {
    this.options = merge({}, defaultConfig, options.metrics);
    this.metrics = {};
    this.storageManager = context.storageManager;
    this.filterRegistry = context.filterRegistry;
  },
  processMessage(message) {
    const filterRegistry = this.filterRegistry;
    // only dance if we all wants to
    if (this.options.filter) {
      if (message.type === 'sitespeedio.setup') {
        const filters = Array.isArray(this.options.filter)
          ? this.options.filter
          : [this.options.filter];

        for (let metric of filters) {
          // for all filters
          // cleaning all filters means (right now) that all
          // metrics are sent
          if (metric === '*+') {
            filterRegistry.clearAll();
          } else if (metric === '*-') {
            // all registered types will be set as unmatching,
            // use it if you want to have a clean filter where
            // all types are removed and then you can add your own
            let types = filterRegistry.getTypes();
            filterRegistry.clearAll();
            for (let type of types) {
              filterRegistry.registerFilterForType('-', type);
            }
          } else {
            let parts = metric.split('.');
            // the type is "always" the first two
            let type = parts.shift() + '.' + parts.shift();
            let filter = parts.join('.');
            let oldFilter = filterRegistry.getFilterForType(type);
            if (oldFilter && typeof oldFilter === 'object') {
              oldFilter.push(filter);
            } else {
              oldFilter = [filter];
            }
            filterRegistry.registerFilterForType(oldFilter, type);
          }
        }
      }
    }
    if (message.type === 'sitespeedio.render') {
      if (this.options.list) {
        this.storageManager.writeData(
          'metrics.txt',
          Object.keys(this.metrics).join('\n')
        );
      }
      if (this.options.filterList) {
        let output = '';
        let filtersByType = filterRegistry.getFilters();
        for (let type of Object.keys(filtersByType)) {
          for (let filters of filtersByType[type]) {
            output += type + '.' + filters + '\n';
          }
        }
        return this.storageManager.writeData('configuredMetrics.txt', output);
      }
    } else if (
      !(
        message.type.endsWith('.summary') ||
        message.type.endsWith('.pageSummary')
      )
    ) {
      return;
    } else {
      let flattenMess = flatten.flattenMessageData(message);
      for (let key of Object.keys(flattenMess)) {
        this.metrics[message.type + '.' + key] = 1;
      }
    }
  },
  config: defaultConfig
};
<commit_msg>fixed the order filtering<commit_after>'use strict';

const flatten = require('../../support/flattenMessage');
const merge = require('lodash.merge');

const defaultConfig = {
  list: false,
  filterList: false
};

module.exports = {
  open(context, options) {
    this.options = merge({}, defaultConfig, options.metrics);
    this.metrics = {};
    this.storageManager = context.storageManager;
    this.filterRegistry = context.filterRegistry;
  },
  processMessage(message) {
    const filterRegistry = this.filterRegistry;

    if (this.options.list) {
      if (
        !(
          message.type.endsWith('.summary') ||
          message.type.endsWith('.pageSummary')
        )
      ) {
        return;
      }
      let flattenMess = flatten.flattenMessageData(message);
      for (let key of Object.keys(flattenMess)) {
        this.metrics[message.type + '.' + key] = 1;
      }
    }

    // only dance if we all wants to
    if (this.options.filter) {
      if (message.type === 'sitespeedio.setup') {
        const filters = Array.isArray(this.options.filter)
          ? this.options.filter
          : [this.options.filter];

        for (let metric of filters) {
          // for all filters
          // cleaning all filters means (right now) that all
          // metrics are sent
          if (metric === '*+') {
            filterRegistry.clearAll();
          } else if (metric === '*-') {
            // all registered types will be set as unmatching,
            // use it if you want to have a clean filter where
            // all types are removed and then you can add your own
            let types = filterRegistry.getTypes();
            filterRegistry.clearAll();
            for (let type of types) {
              filterRegistry.registerFilterForType('-', type);
            }
          } else {
            let parts = metric.split('.');
            // the type is "always" the first two
            let type = parts.shift() + '.' + parts.shift();
            let filter = parts.join('.');
            let oldFilter = filterRegistry.getFilterForType(type);
            if (oldFilter && typeof oldFilter === 'object') {
              oldFilter.push(filter);
            } else {
              oldFilter = [filter];
            }
            filterRegistry.registerFilterForType(oldFilter, type);
          }
        }
      }
    }
    if (message.type === 'sitespeedio.render') {
      if (this.options.list) {
        this.storageManager.writeData(
          'metrics.txt',
          Object.keys(this.metrics).join('\n')
        );
      }
      if (this.options.filterList) {
        let output = '';
        let filtersByType = filterRegistry.getFilters();
        for (let type of Object.keys(filtersByType)) {
          for (let filters of filtersByType[type]) {
            output += type + '.' + filters + '\n';
          }
        }
        return this.storageManager.writeData('configuredMetrics.txt', output);
      }
    }
  },
  config: defaultConfig
};
<|endoftext|>


<commit_before>

module.exports = function (context) {
    return {
        ExpressionStatement(node) {
            const expression = node.expression || {};
            const left = expression.left || {};
            const right = expression.right || {};
            const operator = expression.operator;

            const compareWithMinusOne = right.operator === '-' && right.argument.value === 1;
            const lessThanZero = operator === '<' && right.value === 0;
            const moreOrEqualThanZero = operator === '>=' && right.value === 0;
            const isIndexOfEqualToMinusOne = left
                && left.callee
                && (left.callee.property.name === 'indexOf')
                && (compareWithMinusOne || lessThanZero || moreOrEqualThanZero);

            if (isIndexOfEqualToMinusOne) {
                context.report({
                    node,
                    message: 'Do not use indexOf, instead use includes '
                });
            }
        }
    };
};

module.exports.schema = [];
<commit_msg>prefer-includes: Check BinaryExpression instead of ExpressionStatement<commit_after>

module.exports = function (context) {
    return {
        BinaryExpression(node) {
            const left = node.left || {};
            const right = node.right || {};
            const operator = node.operator;

            const compareWithMinusOne = right.operator === '-' && right.argument.value === 1;
            const lessThanZero = operator === '<' && right.value === 0;
            const moreOrEqualThanZero = operator === '>=' && right.value === 0;
            const isIndexOfEqualToMinusOne = left
                && left.callee
                && (left.callee.property.name === 'indexOf')
                && (compareWithMinusOne || lessThanZero || moreOrEqualThanZero);

            if (isIndexOfEqualToMinusOne) {
                context.report({
                    node,
                    message: 'Do not use indexOf, instead use includes '
                });
            }
        }
    };
};

module.exports.schema = [];
<|endoftext|>


<commit_before>/**
 * Created by vedi on 09/02/15.
 */
'use strict';

var
  _ = require('lodash'),
  HTTP_STATUSES   = require('http-statuses');

function SequelizeDataSource(ModelClass) {
  this.ModelClass = ModelClass;
}

SequelizeDataSource.prototype.initialize = function initialize(options) {
  this.idField = options.idField;
  this.fieldMap = options.fieldMap || {};
  this.modelFieldNames = options.modelFieldNames;
};

SequelizeDataSource.prototype.find = function find(options, callback) {
  var filter = options.filter;
  var fields = options.fields;
  var q = options.q;
  var qFields = options.qFields;
  var sort = options.sort;
  var limit = options.limit;
  var skip = options.skip;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);
  this._applyQ(q, qFields, filter);
  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (sort) {
    query.order = [];
    _.forOwn(sort, function (value, key) {
      query.order.push([key, +value > 0 ? 'ASC' : 'DESC']);
    });
    //query.order = sort;
  }
  query.limit = limit;
  if (skip > 0) {
    query.offset = skip;
  }
  if (queryPipe) {
    queryPipe(query);
  }

  var _this = this;

  this.ModelClass.findAll(query).
    then(function(docs) {
      return _.map(docs, function (doc) {
        return _this.toObject(doc);
      });
    }).
    nodeify(callback);
};

SequelizeDataSource.prototype.findOneAsync = function findOneAsync(options) {
  var filter = options.filter;
  var fields = options.fields;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (queryPipe) {
    queryPipe(query);
  }
  return this.ModelClass.findOne(query);
};

SequelizeDataSource.prototype.create = function create(data, callback) {
  return callback(null, this.ModelClass.build(data));
};

SequelizeDataSource.prototype.saveAsync = function saveAsync(doc) {
  var _this = this;
  return doc.save().
    then(function (newDoc) {
      return _this.findOneAsync({filter: newDoc[_this.idField], fields: _this.fieldMap});
    }).
    then(function (result) {
      return result;
    });
};

SequelizeDataSource.prototype.removeAsync = function removeAsync(doc) {
  return doc.destroy().then(function () {
    return doc;
  });
};

SequelizeDataSource.prototype.countAsync = function countAsync(options) {
  var filter = options.filter;
  var q = options.q;
  var qFields = options.qFields;

  this._normalizeFilter(filter, this.ModelClass);
  this._applyQ(q, qFields, filter);

  var query = {};
  this._normalizeFields(this.modelFieldNames || this.getModelFieldNames(), filter, query, true); // for filtering by associations
  query.where = filter;

  return this.ModelClass.count(query);
};

SequelizeDataSource.prototype.toObject = function toObject(model) {
  return model.get({
    plain: true
  });
};

SequelizeDataSource.prototype.getFieldValue = function getFieldValue(model, fieldName) {
  return this._resolveProp(model, fieldName);
};

SequelizeDataSource.prototype.setFieldValue = function setFieldValue(model, fieldName, value) {
  this._setProp(model, fieldName, value);
};

SequelizeDataSource.prototype.proceedArrayMethod = function proceedArrayMethod(dest, source, methodName, fieldName, req) {
  // TODO: Implement
  //// get sure we have an array
  //if (dest[fieldName] === undefined) {
  //  dest[fieldName] = [];
  //}
  //
  //if (methodName === '$addToSet') {
  //  dest[fieldName].addToSet(source);
  //} else if (methodName === '$pop') {
  //  if (source === 1) {
  //    dest[fieldName].pop();
  //  } else if (source === -1) {
  //    dest[fieldName].shift();
  //  } else {
  //    throw new Error('Illegal param value for $pop method');
  //  }
  //} else if (methodName === '$push') {
  //  dest[fieldName].push(source);
  //} else if (methodName === '$pull') {
  //  dest[fieldName].pull(source);
  //}
};

SequelizeDataSource.prototype.assignField = function assignField(dest, source, fieldName, req, callback) {
  if (_.isFunction(dest.set)) {
    dest.set(fieldName, source[fieldName]);
  } else {
    // on insert we come here
    this._setProp(dest, fieldName, source[fieldName]);
  }
  callback();
};

SequelizeDataSource.prototype.getModelFieldNames = function getModelFieldNames() {
  return _.keys(this.ModelClass.rawAttributes);
};

SequelizeDataSource.prototype.parseError = function parseError(err) {
  // TODO: Implement
  var result = {};
  if (err.name == 'SequelizeValidationError') {
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }
  //else if (err.name == 'CastError') {
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = {};
  //  result.details[err.path] = {
  //    message: err.message,
  //    name: err.name,
  //    path: err.path,
  //    type: err.type,
  //    value: err.value
  //  };
  //  result.message = 'CastError';
  //}
  //else if (err.name == 'MongoError' && (err.code == 11000 || err.code == 11001)) { // E11000(1) duplicate key error index
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = err.err;
  //}
  //else if (err.name == 'VersionError') {
  //  result.status = HTTP_STATUSES.CONFLICT;
  //  result.details = err.message;
  //} else {
  //  return;
  //}
  else {
    // it's just for testing
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }

  return result;
};

SequelizeDataSource.prototype._applyQ = function (q, qFields, filter) {
  var qExpr = q ? this._buildQ(qFields, q) : undefined;
  if (!_.isUndefined(qExpr) && qExpr.length > 0) {
    filter.$or = qExpr;
  }
};

SequelizeDataSource.prototype._buildQ = function _buildQ(qFields, q) {
  return _.collect(qFields, function (qField) {
    return [qField + ' LIKE "%' + q + '%"'];
  });
};

/**
 * @param fields
 * @param filter
 * @param query
 * @param filterDriven if set, `include`s will be created for filtered values only
 * @returns {*}
 * @private
 */
SequelizeDataSource.prototype._normalizeFields = function _normalizeFields(fields, filter, query, filterDriven) {
  query.include = query.include || [];
  fields = this._resolveAssociations(this.ModelClass, this.fieldMap, fields, filter, query.include, filterDriven);

  if (!filterDriven && fields.indexOf(this.ModelClass.primaryKeyField) < 0) {
    fields.push(this.ModelClass.primaryKeyField);
  }

  return fields;
};

SequelizeDataSource.prototype._resolveAssociations = function (ModelClass, fieldMap, fields, filter, include, filterDriven) {
  include = include || [];
  var fieldsToRemove = [];
  var filterToRemove = [];
  var result = _.map(fields, function (field) {
    var isObject = typeof(field) === 'object';
    var fieldName = isObject ? field.name : field;
    var fieldMeta = fieldMap && fieldMap[fieldName];
    var association = ModelClass.associations[fieldName];
    if (fieldMeta && association) {
      if (!filterDriven) {
        fieldsToRemove.push(fieldName);
      }

      var associationFields;
      var required;
      if (isObject && field.fields) {
        associationFields = field.fields;
        required = field.required;
      } else if (fieldMeta.fields) {
        associationFields = fieldMeta.fields;
        required = fieldMeta.required;
      } else {
        associationFields = [association.targetKey];
        required = undefined;
      }

      var filterValue = filter ? filter[fieldName] : undefined;

      var nestedInclude = [];

      associationFields = this._resolveAssociations(association.target, fieldMeta.fields, associationFields, filterValue, nestedInclude, filterDriven);

      var cleanFilterValue = filterValue ? _.transform(filterValue, function (result, value, key) {
        if (associationFields.indexOf(key) >= 0) {
          result[key] = value;
        }
        return result;
      }) : undefined;

      if (cleanFilterValue) {
        filterToRemove.push(fieldName);
      }

      if (!filterDriven || cleanFilterValue) {
        // put to include
        include.push({
          association: association,
          attributes: associationFields,
          include: nestedInclude,
          required: required,
          where: cleanFilterValue
        });
      }
    }
    return fieldName;
  }, this);

  if (!filterDriven) {
    _.each(fieldsToRemove, function (field) {
      _.pull(result, field);
    });
  }

  _.each(filterToRemove, function (filterItem) {
    delete filter[filterItem];
  });

  return result;
};

SequelizeDataSource.prototype._normalizeFilter = function _normalizeFilter(filter, root) {
  // TODO: Implement
  //_.forEach(_.keys(filter), function (key) {
  //  var path = root.schema.paths[key];
  //  // if it's an operator
  //  if (key.substr(0, 1) === '$') {
  //    // increase the level without changing the root
  //    this._normalizeFilter(filter[key], root);
  //  } else if (path) {
  //    var typeName = path.options.type.name;
  //    // it's embedded document
  //    if (!_.isUndefined(path.schema)) {
  //      this._normalizeFilter(filter[key], root.schema.paths[key]);
  //    } else if (typeName === 'ObjectId') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = ObjectID(filter[key]);
  //      }
  //    } else if (typeName === 'Date') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = new Date(filter[key]);
  //      }
  //      else if (typeof(filter[key]) === 'object') {
  //        _.forOwn(filter[key], function (value, innerKey) {
  //          if (typeof(value) === 'string') {
  //            filter[key][innerKey] = new Date(value);
  //          }
  //        });
  //      }
  //    }
  //  }
  //}, this);
};

SequelizeDataSource.prototype._resolveProp = function _resolveProp(obj, stringPath) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj;
};

SequelizeDataSource.prototype._setProp = function _setProp(obj, stringPath, value) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length - 1) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj[pathArray.length ? pathArray[0] : stringPath] = value;
};

SequelizeDataSource.prototype.defaultIdField = 'id';
SequelizeDataSource.prototype.defaultArrayMethods = ['$addToSet', '$pop', '$push', '$pull'];

module.exports = SequelizeDataSource;

<commit_msg>replace `count` with `findAndCountAll`<commit_after>/**
 * Created by vedi on 09/02/15.
 */
'use strict';

var
  _ = require('lodash'),
  HTTP_STATUSES   = require('http-statuses');

function SequelizeDataSource(ModelClass) {
  this.ModelClass = ModelClass;
}

SequelizeDataSource.prototype.initialize = function initialize(options) {
  this.idField = options.idField;
  this.fieldMap = options.fieldMap || {};
  this.modelFieldNames = options.modelFieldNames;
};

SequelizeDataSource.prototype.find = function find(options, callback) {
  var filter = options.filter;
  var fields = options.fields;
  var q = options.q;
  var qFields = options.qFields;
  var sort = options.sort;
  var limit = options.limit;
  var skip = options.skip;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);
  this._applyQ(q, qFields, filter);
  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (sort) {
    query.order = [];
    _.forOwn(sort, function (value, key) {
      query.order.push([key, +value > 0 ? 'ASC' : 'DESC']);
    });
    //query.order = sort;
  }
  query.limit = limit;
  if (skip > 0) {
    query.offset = skip;
  }
  if (queryPipe) {
    queryPipe(query);
  }

  var _this = this;

  this.ModelClass.findAll(query).
    then(function(docs) {
      return _.map(docs, function (doc) {
        return _this.toObject(doc);
      });
    }).
    nodeify(callback);
};

SequelizeDataSource.prototype.findOneAsync = function findOneAsync(options) {
  var filter = options.filter;
  var fields = options.fields;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (queryPipe) {
    queryPipe(query);
  }
  return this.ModelClass.findOne(query);
};

SequelizeDataSource.prototype.create = function create(data, callback) {
  return callback(null, this.ModelClass.build(data));
};

SequelizeDataSource.prototype.saveAsync = function saveAsync(doc) {
  var _this = this;
  return doc.save().
    then(function (newDoc) {
      return _this.findOneAsync({filter: newDoc[_this.idField], fields: _this.fieldMap});
    }).
    then(function (result) {
      return result;
    });
};

SequelizeDataSource.prototype.removeAsync = function removeAsync(doc) {
  return doc.destroy().then(function () {
    return doc;
  });
};

SequelizeDataSource.prototype.countAsync = function countAsync(options) {
  var filter = options.filter;
  var q = options.q;
  var qFields = options.qFields;

  this._normalizeFilter(filter, this.ModelClass);
  this._applyQ(q, qFields, filter);

  var query = {};
  this._normalizeFields(this.modelFieldNames || this.getModelFieldNames(), filter, query); // for filtering by associations
  query.where = filter;

  query.limit = 1;

  // return this.ModelClass.count(query); -- count shows wrong results in some complex cases.
  return this.ModelClass.
    findAndCountAll(query).
    then(function (result) {
      return result.count;
    });
};

SequelizeDataSource.prototype.toObject = function toObject(model) {
  return model.get({
    plain: true
  });
};

SequelizeDataSource.prototype.getFieldValue = function getFieldValue(model, fieldName) {
  return this._resolveProp(model, fieldName);
};

SequelizeDataSource.prototype.setFieldValue = function setFieldValue(model, fieldName, value) {
  this._setProp(model, fieldName, value);
};

SequelizeDataSource.prototype.proceedArrayMethod = function proceedArrayMethod(dest, source, methodName, fieldName, req) {
  // TODO: Implement
  //// get sure we have an array
  //if (dest[fieldName] === undefined) {
  //  dest[fieldName] = [];
  //}
  //
  //if (methodName === '$addToSet') {
  //  dest[fieldName].addToSet(source);
  //} else if (methodName === '$pop') {
  //  if (source === 1) {
  //    dest[fieldName].pop();
  //  } else if (source === -1) {
  //    dest[fieldName].shift();
  //  } else {
  //    throw new Error('Illegal param value for $pop method');
  //  }
  //} else if (methodName === '$push') {
  //  dest[fieldName].push(source);
  //} else if (methodName === '$pull') {
  //  dest[fieldName].pull(source);
  //}
};

SequelizeDataSource.prototype.assignField = function assignField(dest, source, fieldName, req, callback) {
  if (_.isFunction(dest.set)) {
    dest.set(fieldName, source[fieldName]);
  } else {
    // on insert we come here
    this._setProp(dest, fieldName, source[fieldName]);
  }
  callback();
};

SequelizeDataSource.prototype.getModelFieldNames = function getModelFieldNames() {
  return _.keys(this.ModelClass.rawAttributes);
};

SequelizeDataSource.prototype.parseError = function parseError(err) {
  // TODO: Implement
  var result = {};
  if (err.name == 'SequelizeValidationError') {
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }
  //else if (err.name == 'CastError') {
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = {};
  //  result.details[err.path] = {
  //    message: err.message,
  //    name: err.name,
  //    path: err.path,
  //    type: err.type,
  //    value: err.value
  //  };
  //  result.message = 'CastError';
  //}
  //else if (err.name == 'MongoError' && (err.code == 11000 || err.code == 11001)) { // E11000(1) duplicate key error index
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = err.err;
  //}
  //else if (err.name == 'VersionError') {
  //  result.status = HTTP_STATUSES.CONFLICT;
  //  result.details = err.message;
  //} else {
  //  return;
  //}
  else {
    // it's just for testing
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }

  return result;
};

SequelizeDataSource.prototype._applyQ = function (q, qFields, filter) {
  var qExpr = q ? this._buildQ(qFields, q) : undefined;
  if (!_.isUndefined(qExpr) && qExpr.length > 0) {
    filter.$or = qExpr;
  }
};

SequelizeDataSource.prototype._buildQ = function _buildQ(qFields, q) {
  return _.collect(qFields, function (qField) {
    return [qField + ' LIKE "%' + q + '%"'];
  });
};

/**
 * @param fields
 * @param filter
 * @param query
 * @param filterDriven if set, `include`s will be created for filtered values only
 * @returns {*}
 * @private
 */
SequelizeDataSource.prototype._normalizeFields = function _normalizeFields(fields, filter, query, filterDriven) {
  query.include = query.include || [];
  fields = this._resolveAssociations(this.ModelClass, this.fieldMap, fields, filter, query.include, filterDriven);

  if (!filterDriven && fields.indexOf(this.ModelClass.primaryKeyField) < 0) {
    fields.push(this.ModelClass.primaryKeyField);
  }

  return fields;
};

SequelizeDataSource.prototype._resolveAssociations = function (ModelClass, fieldMap, fields, filter, include, filterDriven) {
  include = include || [];
  var fieldsToRemove = [];
  var filterToRemove = [];
  var result = _.map(fields, function (field) {
    var isObject = typeof(field) === 'object';
    var fieldName = isObject ? field.name : field;
    var fieldMeta = fieldMap && fieldMap[fieldName];
    var association = ModelClass.associations[fieldName];
    if (fieldMeta && association) {
      if (!filterDriven) {
        fieldsToRemove.push(fieldName);
      }

      var associationFields;
      var required;
      if (isObject && field.fields) {
        associationFields = field.fields;
        required = field.required;
      } else if (fieldMeta.fields) {
        associationFields = fieldMeta.fields;
        required = fieldMeta.required;
      } else {
        associationFields = [association.targetKey];
        required = undefined;
      }

      var filterValue = filter ? filter[fieldName] : undefined;

      var nestedInclude = [];

      associationFields = this._resolveAssociations(association.target, fieldMeta.fields, associationFields, filterValue, nestedInclude, filterDriven);

      var cleanFilterValue = filterValue ? _.transform(filterValue, function (result, value, key) {
        if (associationFields.indexOf(key) >= 0) {
          result[key] = value;
        }
        return result;
      }) : undefined;

      if (cleanFilterValue) {
        filterToRemove.push(fieldName);
      }

      if (!filterDriven || cleanFilterValue) {
        // put to include
        include.push({
          association: association,
          attributes: associationFields,
          include: nestedInclude,
          required: required,
          where: cleanFilterValue
        });
      }
    }
    return fieldName;
  }, this);

  if (!filterDriven) {
    _.each(fieldsToRemove, function (field) {
      _.pull(result, field);
    });
  }

  _.each(filterToRemove, function (filterItem) {
    delete filter[filterItem];
  });

  return result;
};

SequelizeDataSource.prototype._normalizeFilter = function _normalizeFilter(filter, root) {
  // TODO: Implement
  //_.forEach(_.keys(filter), function (key) {
  //  var path = root.schema.paths[key];
  //  // if it's an operator
  //  if (key.substr(0, 1) === '$') {
  //    // increase the level without changing the root
  //    this._normalizeFilter(filter[key], root);
  //  } else if (path) {
  //    var typeName = path.options.type.name;
  //    // it's embedded document
  //    if (!_.isUndefined(path.schema)) {
  //      this._normalizeFilter(filter[key], root.schema.paths[key]);
  //    } else if (typeName === 'ObjectId') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = ObjectID(filter[key]);
  //      }
  //    } else if (typeName === 'Date') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = new Date(filter[key]);
  //      }
  //      else if (typeof(filter[key]) === 'object') {
  //        _.forOwn(filter[key], function (value, innerKey) {
  //          if (typeof(value) === 'string') {
  //            filter[key][innerKey] = new Date(value);
  //          }
  //        });
  //      }
  //    }
  //  }
  //}, this);
};

SequelizeDataSource.prototype._resolveProp = function _resolveProp(obj, stringPath) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj;
};

SequelizeDataSource.prototype._setProp = function _setProp(obj, stringPath, value) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length - 1) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj[pathArray.length ? pathArray[0] : stringPath] = value;
};

SequelizeDataSource.prototype.defaultIdField = 'id';
SequelizeDataSource.prototype.defaultArrayMethods = ['$addToSet', '$pop', '$push', '$pull'];

module.exports = SequelizeDataSource;

<|endoftext|>


<commit_before>'use strict';

const ServerCodeModel   = require('./model'),
      logger            = require('../util/logger'),
      path              = require('path'),
      fs                = require('fs'),
      file              = require('../util/file'),
      JSZip             = require('jszip'),
      ApiServerService = require('./services/api-server');

exports.publish = function(opts) {
  const apiServer = new ApiServerService(opts.app, opts.backendless.apiServer);

  let registered;
  let zipped;
  let model;

  function buildModel() {
    model = ServerCodeModel.build(process.cwd(), file.expand(opts.app.files, { nodir: true }));

    if (model.isEmpty() && !opts.allowEmpty) {
      throw new Error('Nothing to publish');
    }
  }

  function zip() {
    zipped = generateZip(model, dependencyPatterns().concat(opts.app.files), opts.keepZip);
  }

  function publishHandlers() {
    if (model.handlers.values().length) {
      return apiServer.registerModel(model).then(() => {
        return apiServer.publish(zipped);
      });
    }
  }

  function publishServices() {
    const services = model.services.values();

    if (services.length) {
      return apiServer.unregisterDebugServices()
        .then(() => Promise.all(services.map((s) => {
          return apiServer.publishService(s, zipped);
        })));
    }
  }

  function registerRunner() {
    return apiServer.registerRunner() // http://bugs.backendless.com/browse/BKNDLSS-11655
      .then(() => registered = true);
  }

  function finalize() {
    if (registered) {
      registered = false;

      return apiServer.unregisterRunner();
    }
  }

  return Promise.resolve()
    .then(buildModel)
    .then(zip)
    .then(registerRunner)
    .then(publishHandlers)
    .then(publishServices)
    .then(finalize)
    .catch(err => {
      finalize();

      throw err;
    });
};

function generateZip(model, patterns, keep) {
  logger.info('Preparing app zip file for deployment..');
  logger.debug('File patterns to be included:');

  patterns.forEach(pattern => logger.debug(pattern));

  const zip = new JSZip();
  const expanded = file.expand(patterns);
  let files = 0;

  zip.file('model.json', JSON.stringify(model));

  expanded.forEach(item => {
    if (fs.statSync(item).isDirectory()) {
      zip.folder(item);
    } else {
      zip.file(item, fs.readFileSync(item));
      files++;
    }
  });

  logger.info(`${files} files added into deployment archive`);

  const result = zip.generate({ type: 'nodebuffer' });

  if (keep) {
    fs.writeFile('deploy.zip', result);
    logger.info(`Deployment archive is saved to ${path.resolve('deploy.zip')}`);
  }

  return result;
}

function dependencyPatterns() {
  const pkgFile = path.resolve('package.json');

  if (fs.existsSync(pkgFile)) {
    const pkg = require(pkgFile);

    if (pkg.dependencies) {
      return (Object.keys(pkg.dependencies)).map(dep => {
        return `node_modules/${dep}/**`;
      });
    }
  } else {
    logger.info('Warning. Working directory doesn\'t contain package.json file. ' +
      'CodeRunner is not able to optimize application deployment size');

    return ['node_modules/**'];
  }

  return [];
}<commit_msg>change (publisher) include all non dev dependencies into deployment zip file<commit_after>'use strict';

const ServerCodeModel   = require('./model'),
      logger            = require('../util/logger'),
      path              = require('path'),
      fs                = require('fs'),
      file              = require('../util/file'),
      JSZip             = require('jszip'),
      ApiServerService = require('./services/api-server');

exports.publish = function(opts) {
  const apiServer = new ApiServerService(opts.app, opts.backendless.apiServer);

  let registered;
  let zipped;
  let model;

  function buildModel() {
    model = ServerCodeModel.build(process.cwd(), file.expand(opts.app.files, { nodir: true }));

    if (model.isEmpty() && !opts.allowEmpty) {
      throw new Error('Nothing to publish');
    }
  }

  function zip() {
    zipped = generateZip(model, dependencyPatterns().concat(opts.app.files), opts.keepZip);
  }

  function publishHandlers() {
    if (model.handlers.values().length) {
      return apiServer.registerModel(model).then(() => {
        return apiServer.publish(zipped);
      });
    }
  }

  function publishServices() {
    const services = model.services.values();

    if (services.length) {
      return apiServer.unregisterDebugServices()
        .then(() => Promise.all(services.map((s) => {
          return apiServer.publishService(s, zipped);
        })));
    }
  }

  function registerRunner() {
    return apiServer.registerRunner() // http://bugs.backendless.com/browse/BKNDLSS-11655
      .then(() => registered = true);
  }

  function finalize() {
    if (registered) {
      registered = false;

      return apiServer.unregisterRunner();
    }
  }

  return Promise.resolve()
    .then(buildModel)
    .then(zip)
    .then(registerRunner)
    .then(publishHandlers)
    .then(publishServices)
    .then(finalize)
    .catch(err => {
      finalize();

      throw err;
    });
};

function generateZip(model, patterns, keep) {
  logger.info('Preparing app zip file for deployment..');
  logger.debug('File patterns to be included:');

  patterns.forEach(pattern => logger.debug(pattern));

  const zip = new JSZip();
  const expanded = file.expand(patterns);
  let files = 0;

  zip.file('model.json', JSON.stringify(model));

  expanded.forEach(item => {
    if (fs.statSync(item).isDirectory()) {
      zip.folder(item);
    } else {
      zip.file(item, fs.readFileSync(item));
      files++;
    }
  });

  logger.info(`${files} files added into deployment archive`);

  const result = zip.generate({ type: 'nodebuffer' });

  if (keep) {
    fs.writeFile('deploy.zip', result);
    logger.info(`Deployment archive is saved to ${path.resolve('deploy.zip')}`);
  }

  return result;
}

function dependencyPatterns() {
  const pkgFile = path.resolve('package.json');

  const result = ['node_modules/**'];

  if (fs.existsSync(pkgFile)) {
    const pkg = require(pkgFile);

    if (pkg.devDependencies) {
      Object.keys(pkg.devDependencies).forEach(dep => {
        result.push(`!node_modules/${dep}`);
      });
    }
  } else {
    logger.info('Warning. Working directory doesn\'t contain package.json file. ' +
      'CodeRunner is not able to optimize application deployment size');
  }

  return result;
}<|endoftext|>