<commit_before>'use strict';

var rfr = require('rfr');
var _ = require('lodash');
var mkdirp = require('mkdirp');
var fs = require('fs-extra');
var path = require('path');
var Handlebars = rfr('src/handlebars');
var marked = rfr('src/marked');
var babel = require('babel-core');

var docTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/doc.handlebars'), 'utf8');
var exampleTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/example.handlebars'), 'utf8');
var indexTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/index.handlebars'), 'utf8');

class Generator {

	generate(docs, destination, options = {}) {
		mkdirp(destination, error => {
			if (error) {
				console.error(`Unable to create destination directory: ${error}`, error.stack);
				return;
			}

			docs = _(docs)
				.sortBy('name')
				.map(doc => {
					doc.html = this.generateDoc(doc, destination, options);
					return doc;
				})
				.value();

			let groups = _(docs)
				.groupBy(doc => doc.category || 'Other')
				.map((docs, category) => {
					var order = _.isEmpty(options.order) ? ['-Other'] : options.order;
					var normalizedCategoryOrder = _.map(order, category => category.replace(/^[-+]/, ''));
					var orderIndex = _.indexOf(normalizedCategoryOrder, category);
					var isLast = (orderIndex !== -1) ? order[orderIndex].startsWith('-') : null;

					// Lower ranks will be listed first
					var rank;

					// Explicit category order
					if (orderIndex !== -1) {
						if (isLast) {
							// rank > 0 for explicit categories to list last
							rank = orderIndex + 1;
						}
						else {
							// rank < 0 for explicit categories to list first
							rank = orderIndex - order.length;
						}
					}
					// Unspecified category order
					else {
						rank = 0;
					}

					return {
						rank: rank,
						key: category,
						slug: category.toLowerCase(),
						docs: docs,
					};
				})
				.sortBy(['rank', 'key'])
				.value();

			// Copies stylemark assets
			fs.copy(
				path.resolve(__dirname, 'assets'),
				path.resolve(destination, '_stylemark'),
				error => error ? console.log(error) : null
			);

			// Copies logo asset
			let logo = options.logo || '';
			if (logo && !logo.startsWith('http')) {
				fs.copy(
					path.resolve(options.configDir, logo),
					path.resolve(options.output, logo),
					error => error ? console.log(error) : null
				);
			}

			// Copies favicon asset
			let favicon = options.favicon || '';
			if (favicon && !favicon.startsWith('http')) {
				fs.copy(
					path.resolve(options.configDir, favicon),
					path.resolve(options.output, favicon),
					error => error ? console.log(error) : null
				);
			}

			// Copies user-defined assets
			if (options.assets) {
				_.forEach(options.assets, (asset) => {
					fs.copy(
						path.resolve(options.configDir, asset),
						path.resolve(options.output, asset),
						error => error ? console.log(error) : null
					);
				});
			}

			// Copies theme assets
			if (options.theme.assets) {
				_.forEach(options.theme.assets, (asset) => {
					fs.copy(
						path.resolve(options.configDir, asset),
						path.resolve(options.output, asset),
						error => error ? console.log(error) : null
					);
				});

				options.theme.stylesheets = _(options.theme.assets)
					.filter(asset => _.endsWith(asset, '.css'))
					.map(asset => `<link rel="stylesheet" href="${asset}">`)
					.value();

				options.theme.scripts = _(options.theme.assets)
					.filter(asset => _.endsWith(asset, '.js'))
					.map(asset => `<script src="${asset}"></script>`)
					.value();
			}

			let html = Handlebars.compile(indexTemplate)({
				name: options.name,
				sidebar: options.sidebar,
				logo,
				favicon,
				groups,
				options,
			});

			let filepath = path.resolve(destination, 'index.html');
			fs.writeFile(filepath, html, 'utf8', error => error ? console.log(error) : null);
		});
	}

	generateDoc(doc, destination, options = {}) {
		doc.slug = _.kebabCase(doc.name);

		if (doc.description) {
			// Replaces <example> tags with  tags
			doc.description = doc.description.replace(
				/<example name="([^"]+)"( height="\d+")?><\/example>/g,
				`

						Example

\n`
			);

			// Replaces <info|success|warning|danger> tags with alert divs
			doc.description = doc.description.replace(
				/<(info|success|warning|danger)>([\s\S]*?)<\/\1>/g,
				function(match, tag, content) {
					var html = marked(content);
					var icon = {
						info: 'fa fa-info-circle',
						success: 'fa fa-check-circle',
						warning: 'fa fa-exclamation-triangle',
						danger: 'fa fa-exclamation-circle',
					}[tag];

					return `<div class="alert alert-${tag}"><i class="${icon}" style="float: left; margin-right: 7px;"></i>${html}</div>`;
				}
			);
		}

		// Transpiles JSX examples into vanilla JS React calls
		var webpackAppPath = options.webpackAppPath ? options.webpackAppPath + '.' : '';

		_.forEach(doc.examples, example => {
			example.blocks = _.map(example.blocks, block => {
				if (block.language === 'jsx') {
					block.content = babel.transform(block.content, { presets: ['babel-preset-react'] }).code;

					// Prefixes React.createElement(Component) component names with the exported library name (ie. Library.Component)
					block.content = block.content.replace(
						/React\.createElement\(\s*([A-Z][^,]+),/g,
						'React.createElement(' + webpackAppPath + '$1,'
					);
				}
				return block;
			});
		});

		_.forEach(doc.examples, example => this.generateExample(doc, example, destination, options));

		var output = Handlebars.compile(docTemplate)({ doc });
		return output;
	}

	generateExample(doc, example, destination, options = {}) {
		example.slug = _.kebabCase(example.name);

		var output = Handlebars.compile(exampleTemplate)({ doc, example, options });
		var filepath = path.resolve(destination, `${doc.slug}-${example.slug}.html`);

		fs.writeFile(filepath, output, 'utf8', error => error ? console.log(error) : null);
	}
}

module.exports = new Generator();
<commit_msg>Guard against missing 'theme' config<commit_after>'use strict';

var rfr = require('rfr');
var _ = require('lodash');
var mkdirp = require('mkdirp');
var fs = require('fs-extra');
var path = require('path');
var Handlebars = rfr('src/handlebars');
var marked = rfr('src/marked');
var babel = require('babel-core');

var docTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/doc.handlebars'), 'utf8');
var exampleTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/example.handlebars'), 'utf8');
var indexTemplate = fs.readFileSync(path.resolve(__dirname, 'templates/index.handlebars'), 'utf8');

class Generator {

	generate(docs, destination, options = {}) {
		mkdirp(destination, error => {
			if (error) {
				console.error(`Unable to create destination directory: ${error}`, error.stack);
				return;
			}

			docs = _(docs)
				.sortBy('name')
				.map(doc => {
					doc.html = this.generateDoc(doc, destination, options);
					return doc;
				})
				.value();

			let groups = _(docs)
				.groupBy(doc => doc.category || 'Other')
				.map((docs, category) => {
					var order = _.isEmpty(options.order) ? ['-Other'] : options.order;
					var normalizedCategoryOrder = _.map(order, category => category.replace(/^[-+]/, ''));
					var orderIndex = _.indexOf(normalizedCategoryOrder, category);
					var isLast = (orderIndex !== -1) ? order[orderIndex].startsWith('-') : null;

					// Lower ranks will be listed first
					var rank;

					// Explicit category order
					if (orderIndex !== -1) {
						if (isLast) {
							// rank > 0 for explicit categories to list last
							rank = orderIndex + 1;
						}
						else {
							// rank < 0 for explicit categories to list first
							rank = orderIndex - order.length;
						}
					}
					// Unspecified category order
					else {
						rank = 0;
					}

					return {
						rank: rank,
						key: category,
						slug: category.toLowerCase(),
						docs: docs,
					};
				})
				.sortBy(['rank', 'key'])
				.value();

			// Copies stylemark assets
			fs.copy(
				path.resolve(__dirname, 'assets'),
				path.resolve(destination, '_stylemark'),
				error => error ? console.log(error) : null
			);

			// Copies logo asset
			let logo = options.logo || '';
			if (logo && !logo.startsWith('http')) {
				fs.copy(
					path.resolve(options.configDir, logo),
					path.resolve(options.output, logo),
					error => error ? console.log(error) : null
				);
			}

			// Copies favicon asset
			let favicon = options.favicon || '';
			if (favicon && !favicon.startsWith('http')) {
				fs.copy(
					path.resolve(options.configDir, favicon),
					path.resolve(options.output, favicon),
					error => error ? console.log(error) : null
				);
			}

			// Copies user-defined assets
			if (options.assets) {
				_.forEach(options.assets, (asset) => {
					fs.copy(
						path.resolve(options.configDir, asset),
						path.resolve(options.output, asset),
						error => error ? console.log(error) : null
					);
				});
			}

			// Copies theme assets
			if (_.has(options, 'theme.assets')) {
				_.forEach(options.theme.assets, (asset) => {
					fs.copy(
						path.resolve(options.configDir, asset),
						path.resolve(options.output, asset),
						error => error ? console.log(error) : null
					);
				});

				options.theme.stylesheets = _(options.theme.assets)
					.filter(asset => _.endsWith(asset, '.css'))
					.map(asset => `<link rel="stylesheet" href="${asset}">`)
					.value();

				options.theme.scripts = _(options.theme.assets)
					.filter(asset => _.endsWith(asset, '.js'))
					.map(asset => `<script src="${asset}"></script>`)
					.value();
			}

			let html = Handlebars.compile(indexTemplate)({
				name: options.name,
				sidebar: options.sidebar,
				logo,
				favicon,
				groups,
				options,
			});

			let filepath = path.resolve(destination, 'index.html');
			fs.writeFile(filepath, html, 'utf8', error => error ? console.log(error) : null);
		});
	}

	generateDoc(doc, destination, options = {}) {
		doc.slug = _.kebabCase(doc.name);

		if (doc.description) {
			// Replaces <example> tags with  tags
			doc.description = doc.description.replace(
				/<example name="([^"]+)"( height="\d+")?><\/example>/g,
				`

						Example

\n`
			);

			// Replaces <info|success|warning|danger> tags with alert divs
			doc.description = doc.description.replace(
				/<(info|success|warning|danger)>([\s\S]*?)<\/\1>/g,
				function(match, tag, content) {
					var html = marked(content);
					var icon = {
						info: 'fa fa-info-circle',
						success: 'fa fa-check-circle',
						warning: 'fa fa-exclamation-triangle',
						danger: 'fa fa-exclamation-circle',
					}[tag];

					return `<div class="alert alert-${tag}"><i class="${icon}" style="float: left; margin-right: 7px;"></i>${html}</div>`;
				}
			);
		}

		// Transpiles JSX examples into vanilla JS React calls
		var webpackAppPath = options.webpackAppPath ? options.webpackAppPath + '.' : '';

		_.forEach(doc.examples, example => {
			example.blocks = _.map(example.blocks, block => {
				if (block.language === 'jsx') {
					block.content = babel.transform(block.content, { presets: ['babel-preset-react'] }).code;

					// Prefixes React.createElement(Component) component names with the exported library name (ie. Library.Component)
					block.content = block.content.replace(
						/React\.createElement\(\s*([A-Z][^,]+),/g,
						'React.createElement(' + webpackAppPath + '$1,'
					);
				}
				return block;
			});
		});

		_.forEach(doc.examples, example => this.generateExample(doc, example, destination, options));

		var output = Handlebars.compile(docTemplate)({ doc });
		return output;
	}

	generateExample(doc, example, destination, options = {}) {
		example.slug = _.kebabCase(example.name);

		var output = Handlebars.compile(exampleTemplate)({ doc, example, options });
		var filepath = path.resolve(destination, `${doc.slug}-${example.slug}.html`);

		fs.writeFile(filepath, output, 'utf8', error => error ? console.log(error) : null);
	}
}

module.exports = new Generator();
<|endoftext|>


<commit_before>/**
@module ember-metal
*/

import { assert } from 'ember-metal/debug';
import isEnabled from 'ember-metal/features';
import { meta as metaFor } from 'ember-metal/meta';
import { overrideChains } from 'ember-metal/property_events';
// ..........................................................
// DESCRIPTOR
//

/**
  Objects of this type can implement an interface to respond to requests to
  get and set. The default implementation handles simple properties.

  @class Descriptor
  @private
*/
export function Descriptor() {
  this.isDescriptor = true;
}

const REDEFINE_SUPPORTED = (function () {
  // https://github.com/spalger/kibana/commit/b7e35e6737df585585332857a4c397dc206e7ff9
  var a = Object.create(Object.prototype, {
    prop: {
      configurable: true,
      value: 1
    }
  });

  Object.defineProperty(a, 'prop', {
    configurable: true,
    value: 2
  });

  return a.prop === 2;
}());
// ..........................................................
// DEFINING PROPERTIES API
//

export function MANDATORY_SETTER_FUNCTION(name) {
  function SETTER_FUNCTION(value) {
    assert(`You must use Ember.set() to set the \`${name}\` property (of ${this}) to \`${value}\`.`, false);
  }

  SETTER_FUNCTION.isMandatorySetter = true;
  return SETTER_FUNCTION;
}

export function DEFAULT_GETTER_FUNCTION(name) {
  return function GETTER_FUNCTION() {
    var meta = this['__ember_meta__'];
    return meta && meta.peekValues(name);
  };
}

export function INHERITING_GETTER_FUNCTION(name) {
  function IGETTER_FUNCTION() {
    var proto = Object.getPrototypeOf(this);
    return proto && proto[name];
  }

  IGETTER_FUNCTION.isInheritingGetter = true;
  return IGETTER_FUNCTION;
}

/**
  NOTE: This is a low-level method used by other parts of the API. You almost
  never want to call this method directly. Instead you should use
  `Ember.mixin()` to define new properties.

  Defines a property on an object. This method works much like the ES5
  `Object.defineProperty()` method except that it can also accept computed
  properties and other special descriptors.

  Normally this method takes only three parameters. However if you pass an
  instance of `Descriptor` as the third param then you can pass an
  optional value as the fourth parameter. This is often more efficient than
  creating new descriptor hashes for each property.

  ## Examples

  ```javascript
  // ES5 compatible mode
  Ember.defineProperty(contact, 'firstName', {
    writable: true,
    configurable: false,
    enumerable: true,
    value: 'Charles'
  });

  // define a simple property
  Ember.defineProperty(contact, 'lastName', undefined, 'Jolley');

  // define a computed property
  Ember.defineProperty(contact, 'fullName', Ember.computed(function() {
    return this.firstName+' '+this.lastName;
  }).property('firstName', 'lastName'));
  ```

  @private
  @method defineProperty
  @for Ember
  @param {Object} obj the object to define this property on. This may be a prototype.
  @param {String} keyName the name of the property
  @param {Descriptor} [desc] an instance of `Descriptor` (typically a
    computed property) or an ES5 descriptor.
    You must provide this or `data` but not both.
  @param {*} [data] something other than a descriptor, that will
    become the explicit value of this property.
*/
export function defineProperty(obj, keyName, desc, data, meta) {
  var possibleDesc, existingDesc, watching, value;

  if (!meta) {
    meta = metaFor(obj);
  }
  var watchEntry = meta.peekWatching(keyName);
  possibleDesc = obj[keyName];
  existingDesc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined;

  watching = watchEntry !== undefined && watchEntry > 0;

  if (existingDesc) {
    existingDesc.teardown(obj, keyName);
  }

  if (desc instanceof Descriptor) {
    value = desc;
    if (isEnabled('mandatory-setter')) {
      if (watching) {
        Object.defineProperty(obj, keyName, {
          configurable: true,
          enumerable: true,
          writable: true,
          value: value
        });
      } else {
        obj[keyName] = value;
      }
    } else {
      obj[keyName] = value;
    }
    if (desc.setup) { desc.setup(obj, keyName); }
  } else {
    if (desc == null) {
      value = data;

      if (isEnabled('mandatory-setter')) {
        if (watching) {
          meta.writeValues(keyName, data);

          let defaultDescriptor = {
            configurable: true,
            enumerable: true,
            set: MANDATORY_SETTER_FUNCTION(keyName),
            get: DEFAULT_GETTER_FUNCTION(keyName)
          };

          if (REDEFINE_SUPPORTED) {
            Object.defineProperty(obj, keyName, defaultDescriptor);
          } else {
            handleBrokenPhantomDefineProperty(obj, keyName, defaultDescriptor);
          }
        } else {
          obj[keyName] = data;
        }
      } else {
        obj[keyName] = data;
      }
    } else {
      value = desc;

      // fallback to ES5
      Object.defineProperty(obj, keyName, desc);
    }
  }

  // if key is being watched, override chains that
  // were initialized with the prototype
  if (watching) { overrideChains(obj, keyName, meta); }

  // The `value` passed to the `didDefineProperty` hook is
  // either the descriptor or data, whichever was passed.
  if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); }

  return this;
}

function handleBrokenPhantomDefineProperty(obj, keyName, desc) {
  // https://github.com/ariya/phantomjs/issues/11856
  Object.defineProperty(obj, keyName, { configurable: true, writable: true, value: 'iCry' });
  Object.defineProperty(obj, keyName, desc);
}
<commit_msg>[DOC release] Fixes `defineProperty` computed property code sample<commit_after>/**
@module ember-metal
*/

import { assert } from 'ember-metal/debug';
import isEnabled from 'ember-metal/features';
import { meta as metaFor } from 'ember-metal/meta';
import { overrideChains } from 'ember-metal/property_events';
// ..........................................................
// DESCRIPTOR
//

/**
  Objects of this type can implement an interface to respond to requests to
  get and set. The default implementation handles simple properties.

  @class Descriptor
  @private
*/
export function Descriptor() {
  this.isDescriptor = true;
}

const REDEFINE_SUPPORTED = (function () {
  // https://github.com/spalger/kibana/commit/b7e35e6737df585585332857a4c397dc206e7ff9
  var a = Object.create(Object.prototype, {
    prop: {
      configurable: true,
      value: 1
    }
  });

  Object.defineProperty(a, 'prop', {
    configurable: true,
    value: 2
  });

  return a.prop === 2;
}());
// ..........................................................
// DEFINING PROPERTIES API
//

export function MANDATORY_SETTER_FUNCTION(name) {
  function SETTER_FUNCTION(value) {
    assert(`You must use Ember.set() to set the \`${name}\` property (of ${this}) to \`${value}\`.`, false);
  }

  SETTER_FUNCTION.isMandatorySetter = true;
  return SETTER_FUNCTION;
}

export function DEFAULT_GETTER_FUNCTION(name) {
  return function GETTER_FUNCTION() {
    var meta = this['__ember_meta__'];
    return meta && meta.peekValues(name);
  };
}

export function INHERITING_GETTER_FUNCTION(name) {
  function IGETTER_FUNCTION() {
    var proto = Object.getPrototypeOf(this);
    return proto && proto[name];
  }

  IGETTER_FUNCTION.isInheritingGetter = true;
  return IGETTER_FUNCTION;
}

/**
  NOTE: This is a low-level method used by other parts of the API. You almost
  never want to call this method directly. Instead you should use
  `Ember.mixin()` to define new properties.

  Defines a property on an object. This method works much like the ES5
  `Object.defineProperty()` method except that it can also accept computed
  properties and other special descriptors.

  Normally this method takes only three parameters. However if you pass an
  instance of `Descriptor` as the third param then you can pass an
  optional value as the fourth parameter. This is often more efficient than
  creating new descriptor hashes for each property.

  ## Examples

  ```javascript
  // ES5 compatible mode
  Ember.defineProperty(contact, 'firstName', {
    writable: true,
    configurable: false,
    enumerable: true,
    value: 'Charles'
  });

  // define a simple property
  Ember.defineProperty(contact, 'lastName', undefined, 'Jolley');

  // define a computed property
  Ember.defineProperty(contact, 'fullName', Ember.computed('firstName', 'lastName', function() {
    return this.firstName+' '+this.lastName;
  }));
  ```

  @private
  @method defineProperty
  @for Ember
  @param {Object} obj the object to define this property on. This may be a prototype.
  @param {String} keyName the name of the property
  @param {Descriptor} [desc] an instance of `Descriptor` (typically a
    computed property) or an ES5 descriptor.
    You must provide this or `data` but not both.
  @param {*} [data] something other than a descriptor, that will
    become the explicit value of this property.
*/
export function defineProperty(obj, keyName, desc, data, meta) {
  var possibleDesc, existingDesc, watching, value;

  if (!meta) {
    meta = metaFor(obj);
  }
  var watchEntry = meta.peekWatching(keyName);
  possibleDesc = obj[keyName];
  existingDesc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined;

  watching = watchEntry !== undefined && watchEntry > 0;

  if (existingDesc) {
    existingDesc.teardown(obj, keyName);
  }

  if (desc instanceof Descriptor) {
    value = desc;
    if (isEnabled('mandatory-setter')) {
      if (watching) {
        Object.defineProperty(obj, keyName, {
          configurable: true,
          enumerable: true,
          writable: true,
          value: value
        });
      } else {
        obj[keyName] = value;
      }
    } else {
      obj[keyName] = value;
    }
    if (desc.setup) { desc.setup(obj, keyName); }
  } else {
    if (desc == null) {
      value = data;

      if (isEnabled('mandatory-setter')) {
        if (watching) {
          meta.writeValues(keyName, data);

          let defaultDescriptor = {
            configurable: true,
            enumerable: true,
            set: MANDATORY_SETTER_FUNCTION(keyName),
            get: DEFAULT_GETTER_FUNCTION(keyName)
          };

          if (REDEFINE_SUPPORTED) {
            Object.defineProperty(obj, keyName, defaultDescriptor);
          } else {
            handleBrokenPhantomDefineProperty(obj, keyName, defaultDescriptor);
          }
        } else {
          obj[keyName] = data;
        }
      } else {
        obj[keyName] = data;
      }
    } else {
      value = desc;

      // fallback to ES5
      Object.defineProperty(obj, keyName, desc);
    }
  }

  // if key is being watched, override chains that
  // were initialized with the prototype
  if (watching) { overrideChains(obj, keyName, meta); }

  // The `value` passed to the `didDefineProperty` hook is
  // either the descriptor or data, whichever was passed.
  if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); }

  return this;
}

function handleBrokenPhantomDefineProperty(obj, keyName, desc) {
  // https://github.com/ariya/phantomjs/issues/11856
  Object.defineProperty(obj, keyName, { configurable: true, writable: true, value: 'iCry' });
  Object.defineProperty(obj, keyName, desc);
}
<|endoftext|>


<commit_before>/**
 * Created with WebStorm.
 * Project: MoaJs
 * User: Sergii Danilov
 * Date: 10/31/13
 * Time: 6:10 PM
 */
/*global define:true, module:true*/
/**
 Prototype inheritance and extensions in JavaScript
 @module Moa
 */
(function () {
    "use strict";
    if (!Object.create) {
        Object.create = (function () {
            function F() {}
            return function (o) {
                if (arguments.length !== 1) {
                    throw new Error('Object.create implementation only accepts one parameter.');
                }
                F.prototype = o;
                return new F();
            };
        }());
    }
    var undef,
        fn = 'function',
        ob = 'object',
        un = 'undefined',
        str = 'string',
        map = {},
        mixins = {},
        depthRecursion = 64,
        extend = function (target, source) {
            var prop;
            if (source) {
                for (prop in source) {
                    if (source.hasOwnProperty(prop)) {
                        target[prop] = source[prop];
                    }
                }
                //Some Object methods are not enumerable on Internet Explorer
                target.toString = source.toString;
                target.valueOf = source.valueOf;
                target.toLocaleString = source.toLocaleString;
            }
            return target;
        },
        throwWrongParamsErr = function (method, param) {
            var msg = 'Wrong parameters in ' + method;
            if (param) {
                msg = 'Wrong parameter ' + param + ' in ' + method;
            }
            throw new Error(msg, 'Moa');
        },
        throwWrongType = function (obj, extendType, isMixin) {
            var type = 'Type ';
            if (obj === undef) {
                if (isMixin === true) {
                    type = 'Mixin type ';
                }
                throw new Error(type + extendType + ' not found', 'Moa');
            }
        },
        addMixins = function ($proto, $mixin) {
            var prop,
                value,
                MixFn;
            for (prop in $mixin) {
                value = $mixin[prop];
                MixFn = mixins[value];
                throwWrongType(MixFn, value, true);
                MixFn.call($proto);
                $proto[prop] = new MixFn();
            }
            return $proto;
        },
        build = function (type, base, definition) {
            var basetype,
                $staticMixin,
                $single = definition.$single,
                $static = definition.$static,
                $mixin = definition.$mixin,
                $ctor = definition.$ctor,
                $di = definition.$di,
                $base = {};
            if ($ctor !== undef) {
                delete definition.$ctor;
            } else {
                $ctor = function () {};
            }
            delete definition.$single;
            delete definition.$static;
            delete definition.$mixin;
            delete definition.$extend;
            if ($static !== undef) {
                $staticMixin = $static.$mixin;
                if ($staticMixin !== undef) {
                    delete $static.$mixin;
                    addMixins($ctor, $staticMixin);
                }
                extend($ctor, $static);
            }
            if ($mixin !== undef) {
                definition = extend(addMixins({}, $mixin), definition);
            }
            if (base !== undef) {
                basetype = base.$type;
                definition = extend(Object.create(base.$ctor.prototype), definition);
            }
            definition.getType = function () {
                return type;
            };
            extend($base, definition);
            $ctor.prototype = definition;
            $ctor.prototype.constructor = $ctor;
            if ($single !== undef && $single === true) {
                (function () {
                    var instance = new $ctor();
                    $ctor = function () {
                        return instance;
                    };
                    $ctor.getInstance = function () {
                        return instance;
                    };
                }());
            }
            $base.$ctor = $ctor;
            return {
                $type: type,
                $basetype: basetype,
                $mixin: $mixin,
                $di: $di,
                $ctor: $ctor,
                $base: $base
            };
        },
        resolveDeclaration = function (type, diConfiguration) {
            var configurationProperty, configurationValue, typeObj;
            diConfiguration = diConfiguration || {};
            if (!diConfiguration.$current) {
                diConfiguration.$current = type;
            }
            for (configurationProperty in diConfiguration) {
                if (configurationProperty === '$ctor') {
                    configurationValue = resolveDeclaration(type, diConfiguration[configurationProperty]);
                    delete configurationValue.$current;
                } else {
                    configurationValue = diConfiguration[configurationProperty];
                    switch (typeof configurationValue) {
                    case str:
                        if (configurationProperty === '$current') {
                            configurationValue = {
                                type: configurationValue,
                                instance: 'item',
                                lifestyle: 'transient'
                            };
                        } else {
                            typeObj = map[configurationValue];
                            if (typeObj) {
                                configurationValue = typeObj.$di.$current;
                            }
                        }
                        break;
                    case ob:
                        if (configurationProperty === '$current') {
                            configurationValue.type = type;
                        }
                        if (!configurationValue.type) {
                            throwWrongParamsErr('define', '$di -> type');
                        }
                        if (configurationValue.instance === 'ctor') {
                            delete configurationValue.lifestyle;
                        } else {
                            configurationValue.instance = 'item';
                            if (configurationValue.lifestyle && configurationValue.lifestyle !== 'singleton') {
                                configurationValue.lifestyle = 'transient';
                            }
                        }
                        break;
                    default:
                    }
                }
                diConfiguration[configurationProperty] = configurationValue;
            }
            return diConfiguration;
        },
        /**
         @class Moa
        */
        Moa = {
            /**
             * Define new or inherited type
             * @method define
             * @param type {string} name of object type
             * @param definition {Object / Function} implementation of behavior for current type of object. If it is null - delete declared object
             * @return {function} constructor of defined object type
             */
            define: function (type, definition) {
                var mapObj, baseType, base,
                    len = arguments.length;
                switch (len) {
                case 1:
                    mapObj = map[type];
                    throwWrongType(mapObj, type);
                    break;
                case 2:
                    switch (typeof definition) {
                    case fn:
                        baseType = definition().$extend;
                        if (baseType !== undef) {
                            base = map[baseType];
                            throwWrongType(base, baseType);
                            mapObj = build(type, base, definition(base.$base));
                        } else {
                            mapObj = build(type, undef, definition(undef));
                        }
                        break;
                    case ob:
                        if (definition !== null) {
                            baseType = definition.$extend;
                            if (baseType !== undef) {
                                base = map[baseType];
                                throwWrongType(base, baseType);
                            }
                            mapObj = build(type, base, definition);
                        } else {
                            delete map[type];
                            return undef;
                        }
                        break;
                    default:
                        throwWrongParamsErr('define', 'definition');
                    }
                    map[type] = mapObj;
                    map[type].$di = resolveDeclaration(type, mapObj.$di);
                    break;
                default:
                    throwWrongParamsErr('define');
                }
                return mapObj.$ctor;
            },
            /**
             * Declare new mixin type
             * @method mixin
             * @param mixType {string} name of mixin type
             * @param definition {Function} implementation of behavior for mixin.
             */
            mixin: function (mixType, definition) {
                if (definition !== null) {
                    if (typeof definition !== fn) {
                        throwWrongParamsErr('mixin', 'definition');
                    }
                    mixins[mixType] = definition;
                } else {
                    delete mixins[mixType];
                }
            },
            resolve: function (type, configObj) {
                var item, cnt = 0,
                    mapObj = map[type],
                    len = arguments.length,
                    createItem = function (cParams, cObj, mObj) {
                        var item;
                        if (cParams) {
                            cParams = extend(cParams, cObj);
                            item = new mObj.$ctor(cParams);
                        } else {
                            item = new mObj.$ctor(cObj);
                        }
                        return item;
                    },
                    fnResolveObjConf = function (declaration, fnResolveListConf, mp, ctorParams) {
                        var ctorObj, propObj, mpObj,
                            current = declaration.$current,
                            ctor = declaration.$ctor,
                            prop = declaration;
                        cnt += 1;
                        if (cnt > depthRecursion) {
                            throw new Error('Loop of recursion', 'moa');
                        }
                        if (!current) {
                            current = declaration;
                        }
                        mpObj = mp[current.type];
                        if (!ctor) {
                            ctor = mpObj.$di.$ctor;
                        }
                        if (prop === current) {
                            prop = mpObj.$di;
                        }
                        switch (current.instance) {
                        case 'item':
                            if (ctor) {
                                ctorObj = fnResolveListConf(ctor, fnResolveObjConf, mp);
                            }
                            propObj = fnResolveListConf(prop, fnResolveObjConf, mp);
                            switch (current.lifestyle) {
                            case 'transient':
                                item = createItem(ctorParams, ctorObj, mpObj);
                                break;
                            case 'singleton':
                                if (!current.item) {
                                    current.item = createItem(ctorParams, ctorObj, mpObj);
                                }
                                item = current.item;
                                break;
                            default:
                                throwWrongParamsErr('resolve', type + '::$di::$current::lifestyle');
                            }
                            break;
                        case 'ctor':
                            item = mpObj.$ctor;
                            break;
                        default:
                            throwWrongParamsErr('resolve', type + '::$di::$current::instance');
                        }
                        if (propObj) {
                            item = extend(item, propObj);
                        }
                        return item;
                    },
                    fnResolveListConf = function (objDeclaration, fnResolveObjConf, mp) {
                        var prop, propValue, mpObj,
                            result = {};
                        for (prop in objDeclaration) {
                            if (prop !== '$current' && prop !== '$ctor') {
                                propValue = objDeclaration[prop];
                                if (typeof propValue === 'object') {
                                    mpObj = mp[propValue.type];
                                    if (mpObj) {
                                        result[prop] = fnResolveObjConf(propValue, fnResolveListConf, mp);
                                    } else {
                                        result[prop] = objDeclaration[prop];
                                    }
                                } else {
                                    result[prop] = objDeclaration[prop];
                                }
                            }
                        }
                        return result;
                    };
                if (len !== 1 && len !== 2) {
                    throwWrongParamsErr('resolve');
                }
                if (mapObj) {
                    item = fnResolveObjConf(mapObj.$di, fnResolveListConf, map, configObj);
                } else {
                    item = type;
                }
                return item;
            },
            clear: function () {
                var clearObj = function (obj) {
                    var prop;
                    for (prop in obj) {
                        delete obj[prop];
                    }
                };
                clearObj(map);
                clearObj(mixins);
            },
            /**
             * Return object with lists of types and mixins
             * @method getRegistry
             */
            getRegistry: function () {
                var iterate = function (obj) {
                        var prop, arr = [];
                        for (prop in obj) {
                            arr.push(prop);
                        }
                        return arr;
                    };
                return {
                    type: iterate(map),
                    mixin: iterate(mixins)
                };
            },
            getTypeInfo: function (type) {
                var result,
                    mapObj = map[type];
                throwWrongType(mapObj, type);
                result = extend({}, mapObj);
                delete result.$ctor;
                delete result.$base;
                delete result.$di;
                result.$di = JSON.parse(JSON.stringify(mapObj.$di));
                return result;
            }
        };
    // Return as AMD module or attach to head object
    if (typeof define !== un) {
        define([], function () { return Moa; });
    } else if (typeof window !== un) {
        window.Moa = Moa;
    } else {
        module.exports = Moa;
    }
}());<commit_msg>Improve code<commit_after>/**
 * Created with WebStorm.
 * Project: MoaJs
 * User: Sergii Danilov
 * Date: 10/31/13
 * Time: 6:10 PM
 */
/*global define:true, module:true*/
/**
 Prototype inheritance and extensions in JavaScript
 @module Moa
 */
(function () {
    "use strict";
    if (!Object.create) {
        Object.create = (function () {
            function F() {}
            return function (o) {
                if (arguments.length !== 1) {
                    throw new Error('Object.create implementation only accepts one parameter.');
                }
                F.prototype = o;
                return new F();
            };
        }());
    }
    var undef,
        fn = 'function',
        ob = 'object',
        un = 'undefined',
        str = 'string',
        map = {},
        mixins = {},
        depthRecursion = 64,
        extend = function (target, source) {
            var prop;
            if (source) {
                for (prop in source) {
                    if (source.hasOwnProperty(prop)) {
                        target[prop] = source[prop];
                    }
                }
                //Some Object methods are not enumerable on Internet Explorer
                target.toString = source.toString;
                target.valueOf = source.valueOf;
                target.toLocaleString = source.toLocaleString;
            }
            return target;
        },
        throwWrongParamsErr = function (method, param) {
            var msg = 'Wrong parameters in ' + method;
            if (param) {
                msg = 'Wrong parameter ' + param + ' in ' + method;
            }
            throw new Error(msg, 'Moa');
        },
        throwWrongType = function (obj, extendType, isMixin) {
            var type = 'Type ';
            if (obj === undef) {
                if (isMixin === true) {
                    type = 'Mixin type ';
                }
                throw new Error(type + extendType + ' not found', 'Moa');
            }
        },
        addMixins = function ($proto, $mixin) {
            var prop,
                value,
                MixFn;
            for (prop in $mixin) {
                value = $mixin[prop];
                MixFn = mixins[value];
                throwWrongType(MixFn, value, true);
                MixFn.call($proto);
                $proto[prop] = new MixFn();
            }
            return $proto;
        },
        build = function (type, base, definition) {
            var basetype,
                $staticMixin,
                $single = definition.$single,
                $static = definition.$static,
                $mixin = definition.$mixin,
                $ctor = definition.$ctor,
                $di = definition.$di,
                $base = {};
            if ($ctor !== undef) {
                delete definition.$ctor;
            } else {
                $ctor = function () {};
            }
            delete definition.$single;
            delete definition.$static;
            delete definition.$mixin;
            delete definition.$extend;
            if ($static !== undef) {
                $staticMixin = $static.$mixin;
                if ($staticMixin !== undef) {
                    delete $static.$mixin;
                    addMixins($ctor, $staticMixin);
                }
                extend($ctor, $static);
            }
            if ($mixin !== undef) {
                definition = extend(addMixins({}, $mixin), definition);
            }
            if (base !== undef) {
                basetype = base.$type;
                definition = extend(Object.create(base.$ctor.prototype), definition);
            }
            definition.getType = function () {
                return type;
            };
            extend($base, definition);
            $ctor.prototype = definition;
            $ctor.prototype.constructor = $ctor;
            if ($single !== undef && $single === true) {
                (function () {
                    var instance = new $ctor();
                    $ctor = function () {
                        return instance;
                    };
                    $ctor.getInstance = function () {
                        return instance;
                    };
                }());
            }
            $base.$ctor = $ctor;
            return {
                $type: type,
                $basetype: basetype,
                $mixin: $mixin,
                $di: $di,
                $ctor: $ctor,
                $base: $base
            };
        },
        resolveDeclaration = function (type, diConfiguration) {
            var configurationProperty, configurationValue, typeObj;
            diConfiguration = diConfiguration || {};
            if (!diConfiguration.$current) {
                diConfiguration.$current = type;
            }
            for (configurationProperty in diConfiguration) {
                if (configurationProperty === '$ctor') {
                    configurationValue = resolveDeclaration(type, diConfiguration[configurationProperty]);
                    delete configurationValue.$current;
                } else {
                    configurationValue = diConfiguration[configurationProperty];
                    switch (typeof configurationValue) {
                    case str:
                        if (configurationProperty === '$current') {
                            configurationValue = {
                                type: configurationValue,
                                instance: 'item',
                                lifestyle: 'transient'
                            };
                        } else {
                            typeObj = map[configurationValue];
                            if (typeObj) {
                                configurationValue = typeObj.$di.$current;
                            }
                        }
                        break;
                    case ob:
                        if (configurationProperty === '$current') {
                            configurationValue.type = type;
                        }
                        if (!configurationValue.type) {
                            throwWrongParamsErr('define', '$di -> type');
                        }
                        if (configurationValue.instance === 'ctor') {
                            delete configurationValue.lifestyle;
                        } else {
                            configurationValue.instance = 'item';
                            if (configurationValue.lifestyle && configurationValue.lifestyle !== 'singleton') {
                                configurationValue.lifestyle = 'transient';
                            }
                        }
                        break;
                    default:
                    }
                }
                diConfiguration[configurationProperty] = configurationValue;
            }
            return diConfiguration;
        },
        /**
         @class Moa
        */
        Moa = {
            /**
             * Define new or inherited type
             * @method define
             * @param type {string} name of object type
             * @param definition {Object / Function} implementation of behavior for current type of object. If it is null - delete declared object
             * @return {function} constructor of defined object type
             */
            define: function (type, definition) {
                var mapObj, baseType, base,
                    len = arguments.length;
                switch (len) {
                case 1:
                    mapObj = map[type];
                    throwWrongType(mapObj, type);
                    break;
                case 2:
                    switch (typeof definition) {
                    case fn:
                        baseType = definition().$extend;
                        if (baseType !== undef) {
                            base = map[baseType];
                            throwWrongType(base, baseType);
                            mapObj = build(type, base, definition(base.$base));
                        } else {
                            mapObj = build(type, undef, definition(undef));
                        }
                        break;
                    case ob:
                        if (definition !== null) {
                            baseType = definition.$extend;
                            if (baseType !== undef) {
                                base = map[baseType];
                                throwWrongType(base, baseType);
                            }
                            mapObj = build(type, base, definition);
                        } else {
                            delete map[type];
                            return undef;
                        }
                        break;
                    default:
                        throwWrongParamsErr('define', 'definition');
                    }
                    map[type] = mapObj;
                    map[type].$di = resolveDeclaration(type, mapObj.$di);
                    break;
                default:
                    throwWrongParamsErr('define');
                }
                return mapObj.$ctor;
            },
            /**
             * Declare new mixin type
             * @method mixin
             * @param mixType {string} name of mixin type
             * @param definition {Function} implementation of behavior for mixin.
             */
            mixin: function (mixType, definition) {
                if (definition !== null) {
                    if (typeof definition !== fn) {
                        throwWrongParamsErr('mixin', 'definition');
                    }
                    mixins[mixType] = definition;
                } else {
                    delete mixins[mixType];
                }
            },
            resolve: function (type, configObj) {
                var item, cnt = 0,
                    mapObj = map[type],
                    len = arguments.length,
                    createItem = function (cParams, cObj, mObj) {
                        var item;
                        if (cParams) {
                            cParams = extend(cParams, cObj);
                            item = new mObj.$ctor(cParams);
                        } else {
                            item = new mObj.$ctor(cObj);
                        }
                        return item;
                    },
                    fnResolveObjConf = function (declaration, fnResolveListConf, mp, ctorParams) {
                        var ctorObj, propObj, mpObj,
                            current = declaration.$current,
                            ctor = declaration.$ctor,
                            prop = declaration;
                        cnt += 1;
                        if (cnt > depthRecursion) {
                            throw new Error('Loop of recursion', 'moa');
                        }
                        if (!current) {
                            current = declaration;
                        }
                        mpObj = mp[current.type];
                        if (!ctor) {
                            ctor = mpObj.$di.$ctor;
                        }
                        if (prop === current) {
                            prop = mpObj.$di;
                        }
                        switch (current.instance) {
                        case 'item':
                            if (ctor) {
                                ctorObj = fnResolveListConf(ctor, fnResolveObjConf, mp);
                            }
                            propObj = fnResolveListConf(prop, fnResolveObjConf, mp);
                            switch (current.lifestyle) {
                            case 'transient':
                                item = createItem(ctorParams, ctorObj, mpObj);
                                break;
                            case 'singleton':
                                if (!current.item) {
                                    current.item = createItem(ctorParams, ctorObj, mpObj);
                                }
                                item = current.item;
                                break;
                            default:
                                throwWrongParamsErr('resolve', type + '::$di::$current::lifestyle');
                            }
                            break;
                        case 'ctor':
                            item = mpObj.$ctor;
                            break;
                        default:
                            throwWrongParamsErr('resolve', type + '::$di::$current::instance');
                        }
                        if (propObj) {
                            item = extend(item, propObj);
                        }
                        return item;
                    },
                    fnResolveListConf = function (objDeclaration, fnResolveObjConf, mp) {
                        var prop, propValue, mpObj,
                            result = {};
                        for (prop in objDeclaration) {
                            if (prop !== '$current' && prop !== '$ctor') {
                                propValue = objDeclaration[prop];
                                if (typeof propValue === 'object') {
                                    mpObj = mp[propValue.type];
                                    if (mpObj) {
                                        result[prop] = fnResolveObjConf(propValue, fnResolveListConf, mp);
                                    } else {
                                        result[prop] = propValue;
                                    }
                                } else {
                                    result[prop] = propValue;
                                }
                            }
                        }
                        return result;
                    };
                if (len !== 1 && len !== 2) {
                    throwWrongParamsErr('resolve');
                }
                if (mapObj) {
                    item = fnResolveObjConf(mapObj.$di, fnResolveListConf, map, configObj);
                } else {
                    item = type;
                }
                return item;
            },
            clear: function () {
                var clearObj = function (obj) {
                    var prop;
                    for (prop in obj) {
                        delete obj[prop];
                    }
                };
                clearObj(map);
                clearObj(mixins);
            },
            /**
             * Return object with lists of types and mixins
             * @method getRegistry
             */
            getRegistry: function () {
                var iterate = function (obj) {
                        var prop, arr = [];
                        for (prop in obj) {
                            arr.push(prop);
                        }
                        return arr;
                    };
                return {
                    type: iterate(map),
                    mixin: iterate(mixins)
                };
            },
            getTypeInfo: function (type) {
                var result,
                    mapObj = map[type];
                throwWrongType(mapObj, type);
                result = extend({}, mapObj);
                delete result.$ctor;
                delete result.$base;
                delete result.$di;
                result.$di = JSON.parse(JSON.stringify(mapObj.$di));
                return result;
            }
        };
    // Return as AMD module or attach to head object
    if (typeof define !== un) {
        define([], function () { return Moa; });
    } else if (typeof window !== un) {
        window.Moa = Moa;
    } else {
        module.exports = Moa;
    }
}());<|endoftext|>


<commit_before>var Fibers = Npm.require('fibers');
var eventLogger = Npm.require('debug')('apm:tracer');
var REPITITIVE_EVENTS = {'db': true, 'http': true, 'email': true, 'wait': true, 'async': true};

function Tracer() {

};

//In the future, we might wan't to track inner fiber events too.
//Then we can't serialize the object with methods
//That's why we use this method of returning the data
Tracer.prototype.start = function(session, msg) {
  var traceInfo = {
    _id: session.id + "::" + msg.id,
    session: session.id,
    userId: session.userId,
    id: msg.id,
    events: []
  };

  if(msg.msg == 'method') {
    traceInfo.type = 'method';
    traceInfo.name = msg.method;
  } else if(msg.msg == 'sub') {
    traceInfo.type = 'sub';
    traceInfo.name = msg.name;
  } else {
    return null;
  }

  return traceInfo;
};

Tracer.prototype.event = function(traceInfo, type, data) {
  // do not allow to proceed, if already completed or errored
  var lastEvent = this.getLastEvent(traceInfo);
  if(lastEvent && ['complete', 'error'].indexOf(lastEvent.type) >= 0) {
    return false;
  }

  //expecting a end event
  var eventId = true;

  //specially handling for repitivive events like db, http
  if(REPITITIVE_EVENTS[type]) {
    //can't accept a new start event
    if(traceInfo._lastEventId) {
      return false;
    }
    eventId = traceInfo._lastEventId = Random.id();
  }

  var event = {type: type, at: Apm.syncedDate.getTime()};
  if(data) {
    event.data = data;
  }

  traceInfo.events.push(event);

  eventLogger("%s %s", type, traceInfo._id);
  return eventId;
};

Tracer.prototype.eventEnd = function(traceInfo, eventId, data) {
  if(traceInfo._lastEventId && traceInfo._lastEventId == eventId) {
    var lastEvent = this.getLastEvent(traceInfo);
    var type = lastEvent.type + 'end';
    var event = {type: type, at: Apm.syncedDate.getTime()};
    if(data) {
      event.data = data;
    }
    traceInfo.events.push(event);
    eventLogger("%s %s", type, traceInfo._id);

    traceInfo._lastEventId = null;
    return true;
  } else {
    return false;
  }
};

Tracer.prototype.getLastEvent = function(traceInfo) {
  return traceInfo.events[traceInfo.events.length -1]
};

Tracer.prototype.endLastEvent = function(traceInfo) {
  var lastEvent = this.getLastEvent(traceInfo);
  if(lastEvent && !/end$/.test(lastEvent.type)) {
    forcedEnd = true;
    traceInfo.events.push({
      type: lastEvent.type + 'end',
      at: Apm.syncedDate.getTime()
    });
    return true;
  }
  return false;
};

Tracer.prototype.buildTrace = function(traceInfo) {
  var firstEvent = traceInfo.events[0];
  var lastEvent = traceInfo.events[traceInfo.events.length - 1];
  var processedEvents = [];

  if(firstEvent.type != 'start') {
    console.warn('APM: trace is not started yet');
    return null;
  } else if(lastEvent.type != 'complete' && lastEvent.type != 'error') {
    //trace is not completed or errored yet
    console.warn('APM: trace is not completed or errored yet');
    return null;
  } else {
    //build the metrics
    traceInfo.errored = lastEvent.type == 'error';
    traceInfo.at = firstEvent.at;

    var metrics = {
      total: lastEvent.at - firstEvent.at,
    };

    var totalNonCompute = 0;

    firstEvent = ['start', 0];
    if(traceInfo.events[0].data) firstEvent.push(traceInfo.events[0].data);
    processedEvents.push(firstEvent);

    for(var lc=1; lc < traceInfo.events.length - 1; lc += 2) {
      var prevEventEnd = traceInfo.events[lc-1];
      var startEvent = traceInfo.events[lc];
      var endEvent = traceInfo.events[lc+1];
      var computeTime = startEvent.at - prevEventEnd.at;
      if(computeTime > 0) processedEvents.push(['compute', computeTime]);
      if(!endEvent) {
        console.error('APM: no end event for type: ', startEvent.type);
        return null;
      } else if(endEvent.type != startEvent.type + 'end') {
        console.error('APM: endevent type mismatch: ', startEvent.type, endEvent.type, JSON.stringify(traceInfo));
        return null;
      } else {
        var elapsedTimeForEvent = endEvent.at - startEvent.at
        var currentEvent = [startEvent.type, elapsedTimeForEvent];
        currentEvent.push(_.extend({}, startEvent.data, endEvent.data));
        processedEvents.push(currentEvent);
        metrics[startEvent.type] = metrics[startEvent.type] || 0;
        metrics[startEvent.type] += elapsedTimeForEvent;
        totalNonCompute += elapsedTimeForEvent;
      }
    }

    computeTime = lastEvent.at - traceInfo.events[traceInfo.events.length - 2];
    if(computeTime > 0) processedEvents.push(['compute', computeTime]);

    var lastEventData = [lastEvent.type, 0];
    if(lastEvent.data) lastEventData.push(lastEvent.data);
    processedEvents.push(lastEventData);

    metrics.compute = metrics.total - totalNonCompute;
    traceInfo.metrics = metrics;
    traceInfo.events = processedEvents;
    traceInfo.isEventsProcessed = true;
    return traceInfo;
  }
};

Apm.tracer = new Tracer();
<commit_msg>Update tracer.js<commit_after>var Fibers = Npm.require('fibers');
var eventLogger = Npm.require('debug')('apm:tracer');
var REPITITIVE_EVENTS = {'db': true, 'http': true, 'email': true, 'wait': true, 'async': true};

function Tracer() {

};

//In the future, we might wan't to track inner fiber events too.
//Then we can't serialize the object with methods
//That's why we use this method of returning the data
Tracer.prototype.start = function(session, msg) {
  var traceInfo = {
    _id: session.id + "::" + msg.id,
    session: session.id,
    userId: session.userId,
    id: msg.id,
    events: []
  };

  if(msg.msg == 'method') {
    traceInfo.type = 'method';
    traceInfo.name = msg.method;
  } else if(msg.msg == 'sub') {
    traceInfo.type = 'sub';
    traceInfo.name = msg.name;
  } else {
    return null;
  }

  return traceInfo;
};

Tracer.prototype.event = function(traceInfo, type, data) {
  // do not allow to proceed, if already completed or errored
  var lastEvent = this.getLastEvent(traceInfo);
  if(lastEvent && ['complete', 'error'].indexOf(lastEvent.type) >= 0) {
    return false;
  }

  //expecting a end event
  var eventId = true;

  //specially handling for repitivive events like db, http
  if(REPITITIVE_EVENTS[type]) {
    //can't accept a new start event
    if(traceInfo._lastEventId) {
      return false;
    }
    eventId = traceInfo._lastEventId = Random.id();
  }

  var event = {type: type, at: Apm.syncedDate.getTime()};
  if(data) {
    event.data = data;
  }

  traceInfo.events.push(event);

  eventLogger("%s %s", type, traceInfo._id);
  return eventId;
};

Tracer.prototype.eventEnd = function(traceInfo, eventId, data) {
  if(traceInfo._lastEventId && traceInfo._lastEventId == eventId) {
    var lastEvent = this.getLastEvent(traceInfo);
    var type = lastEvent.type + 'end';
    var event = {type: type, at: Apm.syncedDate.getTime()};
    if(data) {
      event.data = data;
    }
    traceInfo.events.push(event);
    eventLogger("%s %s", type, traceInfo._id);

    traceInfo._lastEventId = null;
    return true;
  } else {
    return false;
  }
};

Tracer.prototype.getLastEvent = function(traceInfo) {
  return traceInfo.events[traceInfo.events.length -1]
};

Tracer.prototype.endLastEvent = function(traceInfo) {
  var lastEvent = this.getLastEvent(traceInfo);
  if(lastEvent && !/end$/.test(lastEvent.type)) {
    traceInfo.events.push({
      type: lastEvent.type + 'end',
      at: Apm.syncedDate.getTime()
    });
    return true;
  }
  return false;
};

Tracer.prototype.buildTrace = function(traceInfo) {
  var firstEvent = traceInfo.events[0];
  var lastEvent = traceInfo.events[traceInfo.events.length - 1];
  var processedEvents = [];

  if(firstEvent.type != 'start') {
    console.warn('APM: trace is not started yet');
    return null;
  } else if(lastEvent.type != 'complete' && lastEvent.type != 'error') {
    //trace is not completed or errored yet
    console.warn('APM: trace is not completed or errored yet');
    return null;
  } else {
    //build the metrics
    traceInfo.errored = lastEvent.type == 'error';
    traceInfo.at = firstEvent.at;

    var metrics = {
      total: lastEvent.at - firstEvent.at,
    };

    var totalNonCompute = 0;

    firstEvent = ['start', 0];
    if(traceInfo.events[0].data) firstEvent.push(traceInfo.events[0].data);
    processedEvents.push(firstEvent);

    for(var lc=1; lc < traceInfo.events.length - 1; lc += 2) {
      var prevEventEnd = traceInfo.events[lc-1];
      var startEvent = traceInfo.events[lc];
      var endEvent = traceInfo.events[lc+1];
      var computeTime = startEvent.at - prevEventEnd.at;
      if(computeTime > 0) processedEvents.push(['compute', computeTime]);
      if(!endEvent) {
        console.error('APM: no end event for type: ', startEvent.type);
        return null;
      } else if(endEvent.type != startEvent.type + 'end') {
        console.error('APM: endevent type mismatch: ', startEvent.type, endEvent.type, JSON.stringify(traceInfo));
        return null;
      } else {
        var elapsedTimeForEvent = endEvent.at - startEvent.at
        var currentEvent = [startEvent.type, elapsedTimeForEvent];
        currentEvent.push(_.extend({}, startEvent.data, endEvent.data));
        processedEvents.push(currentEvent);
        metrics[startEvent.type] = metrics[startEvent.type] || 0;
        metrics[startEvent.type] += elapsedTimeForEvent;
        totalNonCompute += elapsedTimeForEvent;
      }
    }

    computeTime = lastEvent.at - traceInfo.events[traceInfo.events.length - 2];
    if(computeTime > 0) processedEvents.push(['compute', computeTime]);

    var lastEventData = [lastEvent.type, 0];
    if(lastEvent.data) lastEventData.push(lastEvent.data);
    processedEvents.push(lastEventData);

    metrics.compute = metrics.total - totalNonCompute;
    traceInfo.metrics = metrics;
    traceInfo.events = processedEvents;
    traceInfo.isEventsProcessed = true;
    return traceInfo;
  }
};

Apm.tracer = new Tracer();
<|endoftext|>


<commit_before>define(["lib/reqwest", "lib/lodash", "lib/knockout", "githubEvent"],
  function (reqwest, _, ko, GithubEvent) {
    var githubApi = {};

    var rootUrl = "https://api.github.com";

    function getOrganisationMembers(orgName) {
        return reqwest({
            url: 'https://api.github.com/orgs/' + orgName + '/members',
            type: 'json'
        });
    }

    function getUserEvents(username) {
        return reqwest({
            url: 'https://github.com/' + username + '.json?callback=?',
            method: 'get',
            crossOrigin: true,
            headers: { "Accept": "application/json" },
            type: 'jsonp'
        }).then(function (response) {
            return _.map(response, function (response) {
                return new GithubEvent(response);
            });
        });
    }

    // Returns a KO array which is then updated as data arrives
    githubApi.getOrganisationEvents = function (orgName) {
        var events = ko.observableArray([]);

        var loading = 1;

        getOrganisationMembers(orgName).then(function (members) {
            loading = members.length;

            _.each(members, function (member) {
                getUserEvents(member.login).then(function (userEvents) {
                    var combinedEvents = events().concat(userEvents);
                    events(_.sortBy(combinedEvents, function (e) {
                        return e.timestamp;
                    }).reverse());
                    loading--;
                });
            });
        });

        events.loadingComplete = ko.computed(function () {
            return loading === 0;
        });

        return events;
    };

    return githubApi;
});<commit_msg>Minor tidyup on github api code<commit_after>define(["lib/reqwest", "lib/lodash", "lib/knockout", "githubEvent"],
  function (reqwest, _, ko, GithubEvent) {
    var githubApi = {};

    var rootUrl = "https://api.github.com";

    function getOrganisationMembers(orgName) {
        return reqwest({
            url: 'https://api.github.com/orgs/' + orgName + '/members',
            type: 'json'
        });
    }

    function getUserEvents(username) {
        return reqwest({
            url: 'https://github.com/' + username + '.json?callback=?',
            type: 'jsonp'
        }).then(function (response) {
            return _.map(response, function (response) {
                return new GithubEvent(response);
            });
        });
    }

    // Returns a KO array which is then updated as data arrives
    githubApi.getOrganisationEvents = function (orgName) {
        var events = ko.observableArray([]);

        var loading = 1;

        getOrganisationMembers(orgName).then(function (members) {
            loading = members.length;

            _.each(members, function (member) {
                getUserEvents(member.login).then(function (userEvents) {
                    var combinedEvents = events().concat(userEvents);

                    events(_.sortBy(combinedEvents, function (e) {
                        return e.timestamp;
                    }).reverse());

                    loading--;
                });
            });
        });

        events.loadingComplete = ko.computed(function () {
            return loading === 0;
        });

        return events;
    };

    return githubApi;
});<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var async = require('async');
var winston = require('winston');

var SATOSHI_FACTOR = Math.pow(10, 8);

var Trader = module.exports = function (db) {
  if (!db) {
    throw new Error('`db` is required');
  }

  this.db = db;
  this.rates = {};
  this.logger = new (winston.Logger)({
    transports: [new (winston.transports.Console)()]
  });

  this._tradeQueue = [];
};

Trader.prototype._findExchange = function (name) {
  var exchange;

  try {
    exchange = require('lamassu-' + name);
  } catch (err) {
    if (!err.message.match(/Cannot find module/)) {
      throw err;
    }

    exchange = require(path.join(path.dirname(__dirname), 'exchanges', name));
  }

  return exchange;
};

Trader.prototype._findTicker = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.ticker || exchange;
};

Trader.prototype._findTrader = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.trader || exchange;
};

Trader.prototype._findWallet = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.wallet || exchange;
};

Trader.prototype._tradeQueueFiatBalance = function (exchangeRate) {
  var satoshis = this._tradeQueue.reduce(function (memo, rec) {
    return memo + rec.satoshis;
  }, 0);
  return (satoshis / SATOSHI_FACTOR) * exchangeRate;
};

Trader.prototype._consolidateTrades = function () {
  var queue = this._tradeQueue;

  var tradeRec = {
    fiat: 0,
    satoshis: 0,
    currency: this.config.exchanges.settings.currency
  };

  while (true) {
    var lastRec = queue.shift();
    if (!lastRec) {
      break;
    }
    tradeRec.fiat += lastRec.fiat;
    tradeRec.satoshis += lastRec.satoshis;
    tradeRec.currency = lastRec.currency;
  }
  return tradeRec;
};

Trader.prototype._purchase = function (trade) {
  var self = this;
  var rate = self.rate(trade.currency);
  self.tradeExchange.purchase(trade.satoshis, rate, function (err) {
    console.dir(arguments);
    // XXX: don't ignore purchase errors
    self.pollBalance();
  });
};

Trader.prototype.configure = function (config) {
  if (config.exchanges.settings.lowBalanceMargin < 1) {
    throw new Error('`settings.lowBalanceMargin` has to be >= 1');
  }

  var tickerExchangeCode = config.exchanges.plugins.current.ticker;
  var tickerExchangeConfig = config.exchanges.plugins.settings[tickerExchangeCode] || {};
  tickerExchangeConfig.currency = config.exchanges.settings.currency;
  this.tickerExchange = this._findTicker(tickerExchangeCode).factory(tickerExchangeConfig);

  var tradeExchangeCode = config.exchanges.plugins.current.trade;
  if (tradeExchangeCode) {
    var tradeExchangeConfig = config.exchanges.plugins.settings[tradeExchangeCode];
    this.tradeExchange = this._findTrader(tradeExchangeCode).factory(tradeExchangeConfig);
  }

  var transferExchangeCode = config.exchanges.plugins.current.transfer;
  var transferExchangeConfig = config.exchanges.plugins.settings[transferExchangeCode];
  this.transferExchange = this._findWallet(transferExchangeCode).factory(transferExchangeConfig);

  this.config = config;

  this.pollBalance();
  this.pollRate();
};

/**
 * return fiat balance
 *
 * in input to this function, balance has the following parameters...
 *
 * balance.transferBalance - in satoshis
 * balance.tradeBalance    - in USD
 *
 * Have added conversion here, but this really needs to be thought through, lamassu-bitstamp should perhaps
 * return balance in satoshis
 */
Trader.prototype.fiatBalance = function (transferSatoshis, tradeFiat) {
  var rate = this.rate(this.config.exchanges.settings.currency).rate;
  var balance = this.balance;
  var commission = this.config.exchanges.settings.commission;

  if (!rate || !balance) {
    return 0;
  }

  // The rate is actually our commission times real rate.
  rate = commission * rate;

  // `lowBalanceMargin` is our safety net. It's a number > 1, and we divide
  // all our balances by it to provide a safety margin.
  var lowBalanceMargin = this.config.exchanges.settings.lowBalanceMargin;

  // `balance.transferBalance` is the balance of our transfer account (the one
  // we use to send Bitcoins to clients). `transferSatoshis` is the number
  // of satoshis we're expected to send for this transaction. By subtracting
  // them, we get `adjustedTransferBalance`, amount of satoshis we'll have
  // after the transaction.
  var adjustedTransferBalance = balance.transferBalance - transferSatoshis;

  // Since `adjustedTransferBalance` is in Satoshis, we need to turn it into
  // Bitcoins and then fiat to learn how much fiat currency we can exchange.
  //
  // Unit validity proof: [ $ ] = [ (B * 10^8) / 10^8 * $/B ]
  //                      [ $ ] = [ B * $/B ]
  //                      [ $ ] = [ $ ]
  var fiatTransferBalance = ((adjustedTransferBalance / SATOSHI_FACTOR) * rate) / lowBalanceMargin;

  // If this server is also configured to trade received fiat for Bitcoins,
  // we also need to calculate if we have enough funds on our trade exchange.
  if (balance.tradeBalance === null) return fiatTransferBalance;
  var tradeBalance = balance.tradeBalance;

  // We need to secure `tradeFiat` (amount of fiat in this transaction) and
  // enough fiat to cover our trading queue (trades aren't executed immediately).
  var adjustedFiat = tradeFiat + this._tradeQueueFiatBalance(rate);

  // So we subtract `adjustedFiat` from `tradeBalance` and again, apply
  // `lowBalanceMargin`.
  var fiatTradeBalance = (tradeBalance - adjustedFiat) / lowBalanceMargin;

  // And we return the smallest number.
  return Math.min(fiatTransferBalance, fiatTradeBalance);
};

Trader.prototype.sendBitcoins = function (deviceFingerprint, tx, cb) {
  var self = this;

  self.db.summonTransaction(deviceFingerprint, tx, function (err, isNew, txHash) {
    if (err) {
      return cb(err);
    }

    if (isNew) {
      return self.transferExchange.sendBitcoins(
        tx.toAddress,
        tx.satoshis,
        self.config.exchanges.settings.transactionFee,
        function(err, txHash) {
          if (err) {
            self.db.reportTransactionError(tx, err);
            return cb(err);
          }

          cb(null, txHash);
          self.db.completeTransaction(tx, txHash);
          self.pollRate();
        }
      );
    }

    // transaction exists, but txHash might be null,
    // in which case ATM should continue polling
    cb(null, txHash);
  });
};

Trader.prototype.trade = function (fiat, satoshis, currency, callback) {
  this._tradeQueue.push({fiat: fiat, satoshis: satoshis, currency: currency});
  callback(null);
};

Trader.prototype.executeTrades = function () {
  if (!this.tradeExchange) return;

  this.logger.info('checking for trades');

  var trade = this._consolidateTrades();
  this.logger.info('consolidated: ', JSON.stringify(trade));

  if (trade.fiat === 0) {
    this.logger.info('rejecting 0 trade');
    return;
  }

  if (trade.fiat < this.config.exchanges.settings.minimumTradeFiat) {
    // throw it back in the water
    this.logger.info('reject fiat too small');
    this._tradeQueue.unshift(trade);
    return;
  }

  this.logger.info('making a trade: %d', trade.satoshis / Math.pow(10, 8));
  this._purchase(trade);
};

Trader.prototype.startPolling = function () {
  this.pollBalance();
  this.pollRate();
  this.executeTrades();

  this.balanceInterval = setInterval(this.pollBalance.bind(this), 60 * 1000);
  this.rateInterval = setInterval(this.pollRate.bind(this), 60 * 1000);

  // Always start trading, even if we don't have a trade exchange configured,
  // since configuration can always change in `Trader#configure`.
  // `Trader#executeTrades` returns early if we don't have a trade exchange
  // configured at the moment.
  this.tradeInterval = setInterval(
    this.executeTrades.bind(this),
    this.config.exchanges.settings.tradeInterval
  );
};

Trader.prototype.stopPolling = function () {
  clearInterval(this.balanceInterval);
  clearInterval(this.rateInterval);
};

Trader.prototype.pollBalance = function (callback) {
  var self = this;

  self.logger.info('collecting balance');

  async.parallel({
    transferBalance: self.transferExchange.balance.bind(self.transferExchange),
    tradeBalance: function (next) {
      if (!self.tradeExchange) {
        return next(null, null);
      }

      self.tradeExchange.balance(next);
    }
  }, function (err, balance) {
    if (err) {
      return callback && callback(err);
    }

    balance.timestamp = Date.now();
    self.logger.info('Balance update:', balance);
    self.balance = balance;

    return callback && callback(null, balance);
  });
};

Trader.prototype.pollRate = function (callback) {
  var self = this;

  var currency = self.config.exchanges.settings.currency;
  self.logger.info('polling for rate...');
  self.tickerExchange.ticker(currency, function(err, rate) {
    if (err) {
      return callback && callback(err);
    }

    self.logger.info('Rate update:', rate);
    self.rates[currency] = {rate: rate, timestamp: new Date()};
    return callback && callback(null, self.rates[currency]);
  });
};

Trader.prototype.rate = function () {
  return this.rates[this.config.exchanges.settings.currency];
};
<commit_msg>Fix purchase argument<commit_after>'use strict';

var path = require('path');
var async = require('async');
var winston = require('winston');

var SATOSHI_FACTOR = Math.pow(10, 8);

var Trader = module.exports = function (db) {
  if (!db) {
    throw new Error('`db` is required');
  }

  this.db = db;
  this.rates = {};
  this.logger = new (winston.Logger)({
    transports: [new (winston.transports.Console)()]
  });

  this._tradeQueue = [];
};

Trader.prototype._findExchange = function (name) {
  var exchange;

  try {
    exchange = require('lamassu-' + name);
  } catch (err) {
    if (!err.message.match(/Cannot find module/)) {
      throw err;
    }

    exchange = require(path.join(path.dirname(__dirname), 'exchanges', name));
  }

  return exchange;
};

Trader.prototype._findTicker = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.ticker || exchange;
};

Trader.prototype._findTrader = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.trader || exchange;
};

Trader.prototype._findWallet = function (name) {
  var exchange = Trader.prototype._findExchange(name);
  return exchange.wallet || exchange;
};

Trader.prototype._tradeQueueFiatBalance = function (exchangeRate) {
  var satoshis = this._tradeQueue.reduce(function (memo, rec) {
    return memo + rec.satoshis;
  }, 0);
  return (satoshis / SATOSHI_FACTOR) * exchangeRate;
};

Trader.prototype._consolidateTrades = function () {
  var queue = this._tradeQueue;

  var tradeRec = {
    fiat: 0,
    satoshis: 0,
    currency: this.config.exchanges.settings.currency
  };

  while (true) {
    var lastRec = queue.shift();
    if (!lastRec) {
      break;
    }
    tradeRec.fiat += lastRec.fiat;
    tradeRec.satoshis += lastRec.satoshis;
    tradeRec.currency = lastRec.currency;
  }
  return tradeRec;
};

Trader.prototype._purchase = function (trade) {
  var self = this;
  var rate = self.rate(trade.currency);
  self.tradeExchange.purchase(trade.satoshis, rate.rate, function (err) {
    // XXX: don't ignore purchase errors
    self.pollBalance();
  });
};

Trader.prototype.configure = function (config) {
  if (config.exchanges.settings.lowBalanceMargin < 1) {
    throw new Error('`settings.lowBalanceMargin` has to be >= 1');
  }

  var tickerExchangeCode = config.exchanges.plugins.current.ticker;
  var tickerExchangeConfig = config.exchanges.plugins.settings[tickerExchangeCode] || {};
  tickerExchangeConfig.currency = config.exchanges.settings.currency;
  this.tickerExchange = this._findTicker(tickerExchangeCode).factory(tickerExchangeConfig);

  var tradeExchangeCode = config.exchanges.plugins.current.trade;
  if (tradeExchangeCode) {
    var tradeExchangeConfig = config.exchanges.plugins.settings[tradeExchangeCode];
    this.tradeExchange = this._findTrader(tradeExchangeCode).factory(tradeExchangeConfig);
  }

  var transferExchangeCode = config.exchanges.plugins.current.transfer;
  var transferExchangeConfig = config.exchanges.plugins.settings[transferExchangeCode];
  this.transferExchange = this._findWallet(transferExchangeCode).factory(transferExchangeConfig);

  this.config = config;

  this.pollBalance();
  this.pollRate();
};

/**
 * return fiat balance
 *
 * in input to this function, balance has the following parameters...
 *
 * balance.transferBalance - in satoshis
 * balance.tradeBalance    - in USD
 *
 * Have added conversion here, but this really needs to be thought through, lamassu-bitstamp should perhaps
 * return balance in satoshis
 */
Trader.prototype.fiatBalance = function (transferSatoshis, tradeFiat) {
  var rate = this.rate(this.config.exchanges.settings.currency).rate;
  var balance = this.balance;
  var commission = this.config.exchanges.settings.commission;

  if (!rate || !balance) {
    return 0;
  }

  // The rate is actually our commission times real rate.
  rate = commission * rate;

  // `lowBalanceMargin` is our safety net. It's a number > 1, and we divide
  // all our balances by it to provide a safety margin.
  var lowBalanceMargin = this.config.exchanges.settings.lowBalanceMargin;

  // `balance.transferBalance` is the balance of our transfer account (the one
  // we use to send Bitcoins to clients). `transferSatoshis` is the number
  // of satoshis we're expected to send for this transaction. By subtracting
  // them, we get `adjustedTransferBalance`, amount of satoshis we'll have
  // after the transaction.
  var adjustedTransferBalance = balance.transferBalance - transferSatoshis;

  // Since `adjustedTransferBalance` is in Satoshis, we need to turn it into
  // Bitcoins and then fiat to learn how much fiat currency we can exchange.
  //
  // Unit validity proof: [ $ ] = [ (B * 10^8) / 10^8 * $/B ]
  //                      [ $ ] = [ B * $/B ]
  //                      [ $ ] = [ $ ]
  var fiatTransferBalance = ((adjustedTransferBalance / SATOSHI_FACTOR) * rate) / lowBalanceMargin;

  // If this server is also configured to trade received fiat for Bitcoins,
  // we also need to calculate if we have enough funds on our trade exchange.
  if (balance.tradeBalance === null) return fiatTransferBalance;
  var tradeBalance = balance.tradeBalance;

  // We need to secure `tradeFiat` (amount of fiat in this transaction) and
  // enough fiat to cover our trading queue (trades aren't executed immediately).
  var adjustedFiat = tradeFiat + this._tradeQueueFiatBalance(rate);

  // So we subtract `adjustedFiat` from `tradeBalance` and again, apply
  // `lowBalanceMargin`.
  var fiatTradeBalance = (tradeBalance - adjustedFiat) / lowBalanceMargin;

  // And we return the smallest number.
  return Math.min(fiatTransferBalance, fiatTradeBalance);
};

Trader.prototype.sendBitcoins = function (deviceFingerprint, tx, cb) {
  var self = this;

  self.db.summonTransaction(deviceFingerprint, tx, function (err, isNew, txHash) {
    if (err) {
      return cb(err);
    }

    if (isNew) {
      return self.transferExchange.sendBitcoins(
        tx.toAddress,
        tx.satoshis,
        self.config.exchanges.settings.transactionFee,
        function(err, txHash) {
          if (err) {
            self.db.reportTransactionError(tx, err);
            return cb(err);
          }

          cb(null, txHash);
          self.db.completeTransaction(tx, txHash);
          self.pollRate();
        }
      );
    }

    // transaction exists, but txHash might be null,
    // in which case ATM should continue polling
    cb(null, txHash);
  });
};

Trader.prototype.trade = function (fiat, satoshis, currency, callback) {
  this._tradeQueue.push({fiat: fiat, satoshis: satoshis, currency: currency});
  callback(null);
};

Trader.prototype.executeTrades = function () {
  if (!this.tradeExchange) return;

  this.logger.info('checking for trades');

  var trade = this._consolidateTrades();
  this.logger.info('consolidated: ', JSON.stringify(trade));

  if (trade.fiat === 0) {
    this.logger.info('rejecting 0 trade');
    return;
  }

  if (trade.fiat < this.config.exchanges.settings.minimumTradeFiat) {
    // throw it back in the water
    this.logger.info('reject fiat too small');
    this._tradeQueue.unshift(trade);
    return;
  }

  this.logger.info('making a trade: %d', trade.satoshis / Math.pow(10, 8));
  this._purchase(trade);
};

Trader.prototype.startPolling = function () {
  this.pollBalance();
  this.pollRate();
  this.executeTrades();

  this.balanceInterval = setInterval(this.pollBalance.bind(this), 60 * 1000);
  this.rateInterval = setInterval(this.pollRate.bind(this), 60 * 1000);

  // Always start trading, even if we don't have a trade exchange configured,
  // since configuration can always change in `Trader#configure`.
  // `Trader#executeTrades` returns early if we don't have a trade exchange
  // configured at the moment.
  this.tradeInterval = setInterval(
    this.executeTrades.bind(this),
    this.config.exchanges.settings.tradeInterval
  );
};

Trader.prototype.stopPolling = function () {
  clearInterval(this.balanceInterval);
  clearInterval(this.rateInterval);
};

Trader.prototype.pollBalance = function (callback) {
  var self = this;

  self.logger.info('collecting balance');

  async.parallel({
    transferBalance: self.transferExchange.balance.bind(self.transferExchange),
    tradeBalance: function (next) {
      if (!self.tradeExchange) {
        return next(null, null);
      }

      self.tradeExchange.balance(next);
    }
  }, function (err, balance) {
    if (err) {
      return callback && callback(err);
    }

    balance.timestamp = Date.now();
    self.logger.info('Balance update:', balance);
    self.balance = balance;

    return callback && callback(null, balance);
  });
};

Trader.prototype.pollRate = function (callback) {
  var self = this;

  var currency = self.config.exchanges.settings.currency;
  self.logger.info('polling for rate...');
  self.tickerExchange.ticker(currency, function(err, rate) {
    if (err) {
      return callback && callback(err);
    }

    self.logger.info('Rate update:', rate);
    self.rates[currency] = {rate: rate, timestamp: new Date()};
    return callback && callback(null, self.rates[currency]);
  });
};

Trader.prototype.rate = function () {
  return this.rates[this.config.exchanges.settings.currency];
};
<|endoftext|>


<commit_before>module.exports = {
  "baseSelector": ".icon",
  "classPrefix": "icon-",
  "cssTemplate": "./talendicons.template.css",
  "fileName": "[fontname][ext]",
  "files": [
    "svg/*.svg"
  ],
  "fixedWidth": true,
  "fontName": "talendicons"
};
<commit_msg>fix(icons) generate webfont with [fontname].[ext] format (#1998)<commit_after>module.exports = {
	baseSelector: '.icon',
	classPrefix: 'icon-',
	cssTemplate: './talendicons.template.css',
	types: ['eot', 'woff', 'woff2', 'ttf', 'svg'],
	fileName: '[fontname].[ext]',
	files: ['svg/*.svg'],
	fixedWidth: true,
	fontName: 'talendicons',
};
<|endoftext|>