<commit_before>"use strict";

var path = require('path');
var fs = require('fs-extra');
var bower = require('bower');
var colors = require('colors/safe');
var glob = require("glob");

//Regex
var startWithSlash = new RegExp('^\/.*');
var containAsterisk = new RegExp('\\*', 'g');
var containDoubleAsterisk = new RegExp('[\\*]{2}', 'g');

//TODO find is runAsCli is important
var runAsCli = cliDetection();
var commandList = ['install', 'update'];
var bowerPath = './';
var bowerMessage;

var bowerFolder = "./bower_components";

var option = {
	"default": "",
	"min": false,
	"removeAfter": false,
	"minFolder": "",
	"copyFolder": ""
};
var folder = {};
var source = {};

// Call autoRun function at start to detect if call by cli or by script
autoRun();

/**
 * This function is call at start to detect if it will be in mode CLI or API
 */
function autoRun() {
	if (runAsCli) {
		getCommandLineArgument();
	}
}

/**
 * Detect if the script was call by cli or script
 *
 * @returns {boolean}
 */
function cliDetection() {
	// True if call by cli
	return !module.parent;
}

/**
 * Parse the arguments from the console call, execute them and call the clean-bower-install execution
 */
function getCommandLineArgument() {
	var length = process.argv.length;
	if (length > 2) {
		var argumentList = [];

		for (var i = 2; i < length; i++) {
			var toExecute = verify(process.argv[i]);
			if (toExecute !== false) {
				if (toExecute == 'bowerPath') {
					bowerPath = path.join(__dirname, process.argv[i]);
				}

				argumentList.push(toExecute);
			}
		}

		// Verify if the command 'install' and 'update' were both argument of the command
		if (argumentList.indexOf('install') !== -1 && (argumentList.indexOf('update') !== -1)) {
			throw new Error('Error: The command line don\'t accept the two command \'install\' and \'update\' at the same time.');
		} else {
			var commandLength = argumentList.length;
			for (var y = 0; y < commandLength; y++) {
				if (argumentList[y] != 'bowerPath') {
					execute(argumentList[y]);
				}
			}
		}
	} else {
		runCBI();
	}
}

/**
 * Verify that the command exist in the list
 *
 * @param command {string}
 * @returns {string|boolean}
 */
function verify(command) {
	if (command === undefined) {
		return false;
	} else if (commandList.indexOf(command) != -1) {
		return command;
	} else {
		if (testIfPathExist(command)) {
			return 'bowerPath';
		} else {
			return false;
		}
	}
}

/**
 * Execute a command
 *
 * @param command {string}
 */
function execute(command) {
	switch (command) {
		case 'install':
			if (runAsCli) {
				bower.commands.
					install(undefined, undefined, { cwd: bowerPath }).
					on('end', function(installed) {
						if (Object.keys(installed).length !== 0) {
							console.log(installed);
						}
						runCBI();
					});
			} else {
				bower.commands.
					install(undefined, undefined, { cwd: bowerPath, json: true }).
					on('end', function(installed) {
						bowerMessage = installed;
						runCBI();
					});
			}
			break;
		case 'update':
			if (runAsCli) {
				bower.commands.
					update(undefined, undefined, { cwd: bowerPath }).
					on('end', function(installed) {
						if (Object.keys(installed).length !== 0) {
							console.log(installed);
						}
						runCBI();
					});
			} else {
				bower.commands.
					update(undefined, undefined, { cwd: bowerPath, json: true }).
					on('end', function(installed) {
						bowerMessage = installed;
						runCBI();
					});
			}
			break;
		default :
			throw new Error('Error: Unrecognised command.');
	}
}

/**
 * Verify that the specified path exist
 *
 * @param path {string}
 * @returns {boolean}
 */
function testIfPathExist(path) {
	return fs.existsSync(path);
}

/* Execution */

/**
 * Clean-Bower-Install main logic function
 */
function runCBI() {
	if (testIfPathExist(bowerPath)) {
		try {
			var bowerJSON = require(bowerPath + 'bower.json');
		} catch (e) {
			console.log(colors.yellow('clean-bower-install execution can not be done because of that error: ' + e));
		}
	}

	var cInstall = bowerJSON.cInstall;

	if (cInstall !== undefined) {
		if (cInstall.option !== undefined) {
			// To add only new option specified by the user
			for (var setup in cInstall.option) {
				if (cInstall.option.hasOwnProperty(setup) && option.hasOwnProperty(setup)) {
					option[setup] = cInstall.option[setup];
				}
			}
		}

		if (cInstall.folder !== undefined) {
			folder = cInstall.folder;
			var typeInTreatment;
			for (var fileType in folder) {
				if (folder.hasOwnProperty(fileType)) {
					typeInTreatment = fileType.replace(/\s/g, '').split(',');

					// Split into multiple value comma divided chunk
					var length = typeInTreatment.length;
					if (length > 1) {
						for (var i = 0; i < length; i++) {
							folder[typeInTreatment[i]] = folder[fileType];
						}
					}
				}
			}
		}

		if (cInstall.source !== undefined) {
			source = cInstall.source;
			// TODO source option
		}

		moveFiles();

	} else {
		console.log(colors.yellow('clean-bower-install execution can not be done because no \'cInstall\' section were found in the bower.json file.'));
	}
}

/**
 * Move the files from the bower repository to their destination
 */
function moveFiles() {
	var file, length, i,
		filesToMove = generateFileList();

	if (filesToMove === false) {
		return;
	}

	filesToMove = removeDuplicate(filesToMove);

	length = filesToMove.length;
	for (i = 0; i < length; i++) {
		file = filesToMove[i];
		fs.copy(file.from, path.join(file.to, file.rename), function(err) {
			if (err) {
				return console.error(err);
			}
		});
	}
}

function generateFileList() {
	var filesToMove = [], libFolder = '', libName = '', file, fileNameAndExt, fileName, fileFolder,
		extension, temp, currLib, length, i, asteriskName;

	for (var lib in source) {
		if (source.hasOwnProperty(lib)) {
			// TODO work here
			temp = lib.split('#');
			libName = temp[0];
			libFolder = temp[1] || '';
			currLib = source[lib];

			// For each files to get in the lib
			for (file in currLib) {
				if (currLib.hasOwnProperty(file)) {
					temp = file.split('#');
					fileNameAndExt = temp[0] || '*';
					fileName = path.basename(fileNameAndExt, path.extname(fileNameAndExt));
					fileFolder = temp[1] || '';
					extension = path.extname(fileNameAndExt).substr(1);

					// Because of the rename function and the folder specification, the Globstar" ** matching of glob can't be use
					if (containDoubleAsterisk.test(currLib[file])) {
						console.error(colors.red('The "Globstar" ** matching wan\'t support by CLEAN-BOWER-INSTALLER. You have to specify each folder and their destination if you really need it.'));
						console.error(colors.red('Please correct the source: '+ libName));
						return false;
					}

					// List files who fit the
					var files = glob.sync(path.join(bowerFolder, libName, currLib[file]));

					// Add the needed folders to the Array filesToMove
					length = files.length;
					asteriskName = false;
					for (i = 0; i < length; i++) {
						if (containAsterisk.test(fileName)) {
							asteriskName = true;
						}

						if (asteriskName) {
							fileName = path.basename(files[i], path.extname(files[i]));
						}

						// Test if the link is global or relative
						if (startWithSlash.test(fileFolder)) {
							// The specified file folder is global
							filesToMove.push({
								'from': files[i],
								'to': fileFolder.substr(1),
								'rename': fileName + path.extname(files[i])
							});
						} else if (startWithSlash.test(libFolder)) {
							// The specified lib folder is global
							filesToMove.push({
								'from': files[i],
								'to': path.join(libFolder.substr(1), fileFolder),
								'rename': fileName + path.extname(files[i])
							});
						} else {
							// None of the file or lib specified folder is global
							filesToMove.push({
								'from': files[i],
								'to': path.join(option.default, folder[path.extname(files[i]).substr(1)], libFolder, fileFolder),
								'rename': fileName + path.extname(files[i])
							});
						}
					}
				}
			}
		}
	}

	return filesToMove;
}

/**
 * Remove duplicate input in a array and short it
 *
 * @param a {Array}
 * @returns {Array}
 */
function removeDuplicate(a) {
	return a.sort().filter(function(item, pos) {
		return !pos || item != a[pos - 1];
	})
}

/* API commands*/
/**
 * API command to run clean-bower-install with a bower.json in the same folder
 *
 * @param [command] {string}
 */
function run(command) {
	runAsCli = false;

	if (verify(command) !== false) {
		if (command != 'bowerPath') {
			execute(command);
		}
	}

	if (command === undefined) {
		runCBI();
	}
}

/**
 * API command to run clean-bower-install with a bower.json in a specified folder
 *
 * @param relativePath {string}
 * @param [command] {string}
 */
function runFrom(relativePath, command) {
	runAsCli = false;

	var bowerFilePath = path.join(__dirname, relativePath);

	// Error handling
	if (relativePath === undefined) {
		throw new Error('Wrong call to clean-bower-install runFrom() function: No path provided.');
	}

	if (!testIfPathExist(bowerFilePath)) {
		throw new Error('Can not find the file bower.json at the specified path.');
	}

	// Execution
	bowerPath = bowerFilePath;

	if (verify(command) !== false) {
		if (command != 'bowerPath') {
			execute(command);
		}
	}

	if (command === undefined) {
		runCBI();
	}
}

exports.run = run;
exports.runFrom = runFrom;<commit_msg>Repair bower relative position.<commit_after>"use strict";

var path = require('path');
var fs = require('fs-extra');
var bower = require('bower');
var colors = require('colors/safe');
var glob = require("glob");

//Regex
var startWithSlash = new RegExp('^\/.*');
var containAsterisk = new RegExp('\\*', 'g');
var containDoubleAsterisk = new RegExp('[\\*]{2}', 'g');

//TODO find is runAsCli is important
var runAsCli = cliDetection();
var commandList = ['install', 'update'];
var bowerPath = './';
var bowerMessage;

var bowerFolder = "./bower_components";

var option = {
	"default": "",
	"min": false,
	"removeAfter": false,
	"minFolder": "",
	"copyFolder": ""
};
var folder = {};
var source = {};

// Call autoRun function at start to detect if call by cli or by script
autoRun();

/**
 * This function is call at start to detect if it will be in mode CLI or API
 */
function autoRun() {
	if (runAsCli) {
		getCommandLineArgument();
	}
}

/**
 * Detect if the script was call by cli or script
 *
 * @returns {boolean}
 */
function cliDetection() {
	// True if call by cli
	return !module.parent;
}

/**
 * Parse the arguments from the console call, execute them and call the clean-bower-install execution
 */
function getCommandLineArgument() {
	var length = process.argv.length;
	if (length > 2) {
		var argumentList = [];

		for (var i = 2; i < length; i++) {
			var toExecute = verify(process.argv[i]);
			if (toExecute !== false) {
				if (toExecute == 'bowerPath') {
					bowerPath = path.join(__dirname, process.argv[i]);
				}

				argumentList.push(toExecute);
			}
		}

		// Verify if the command 'install' and 'update' were both argument of the command
		if (argumentList.indexOf('install') !== -1 && (argumentList.indexOf('update') !== -1)) {
			throw new Error('Error: The command line don\'t accept the two command \'install\' and \'update\' at the same time.');
		} else {
			var commandLength = argumentList.length;
			for (var y = 0; y < commandLength; y++) {
				if (argumentList[y] != 'bowerPath') {
					execute(argumentList[y]);
				}
			}
		}
	} else {
		runCBI();
	}
}

/**
 * Verify that the command exist in the list
 *
 * @param command {string}
 * @returns {string|boolean}
 */
function verify(command) {
	if (command === undefined) {
		return false;
	} else if (commandList.indexOf(command) != -1) {
		return command;
	} else {
		if (testIfPathExist(command)) {
			return 'bowerPath';
		} else {
			return false;
		}
	}
}

/**
 * Execute a command
 *
 * @param command {string}
 */
function execute(command) {
	switch (command) {
		case 'install':
			if (runAsCli) {
				bower.commands.
					install(undefined, undefined, { cwd: bowerPath }).
					on('end', function(installed) {
						if (Object.keys(installed).length !== 0) {
							console.log(installed);
						}
						runCBI();
					});
			} else {
				bower.commands.
					install(undefined, undefined, { cwd: bowerPath, json: true }).
					on('end', function(installed) {
						bowerMessage = installed;
						runCBI();
					});
			}
			break;
		case 'update':
			if (runAsCli) {
				bower.commands.
					update(undefined, undefined, { cwd: bowerPath }).
					on('end', function(installed) {
						if (Object.keys(installed).length !== 0) {
							console.log(installed);
						}
						runCBI();
					});
			} else {
				bower.commands.
					update(undefined, undefined, { cwd: bowerPath, json: true }).
					on('end', function(installed) {
						bowerMessage = installed;
						bowerFolder = path.join(bowerPath, bowerFolder);
						runCBI();
					});
			}
			break;
		default :
			throw new Error('Error: Unrecognised command.');
	}
}

/**
 * Verify that the specified path exist
 *
 * @param path {string}
 * @returns {boolean}
 */
function testIfPathExist(path) {
	return fs.existsSync(path);
}

/* Execution */

/**
 * Clean-Bower-Install main logic function
 */
function runCBI() {
	if (testIfPathExist(bowerPath)) {
		try {
			var bowerJSON = require(bowerPath + 'bower.json');
		} catch (e) {
			console.log(colors.yellow('clean-bower-install execution can not be done because of that error: ' + e));
		}
	}

	// TODO read .bowerrc

	var cInstall = bowerJSON.cInstall;

	if (cInstall !== undefined) {
		if (cInstall.option !== undefined) {
			// To add only new option specified by the user
			for (var setup in cInstall.option) {
				if (cInstall.option.hasOwnProperty(setup) && option.hasOwnProperty(setup)) {
					option[setup] = cInstall.option[setup];
				}
			}
		}

		if (cInstall.folder !== undefined) {
			folder = cInstall.folder;
			var typeInTreatment;
			for (var fileType in folder) {
				if (folder.hasOwnProperty(fileType)) {
					typeInTreatment = fileType.replace(/\s/g, '').split(',');

					// Split into multiple value comma divided chunk
					var length = typeInTreatment.length;
					if (length > 1) {
						for (var i = 0; i < length; i++) {
							folder[typeInTreatment[i]] = folder[fileType];
						}
					}
				}
			}
		}

		if (cInstall.source !== undefined) {
			source = cInstall.source;
			// TODO source option
		}

		moveFiles();

	} else {
		console.log(colors.yellow('clean-bower-install execution can not be done because no \'cInstall\' section were found in the bower.json file.'));
	}
}

/**
 * Move the files from the bower repository to their destination
 */
function moveFiles() {
	var file, length, i,
		filesToMove = generateFileList();

	if (filesToMove === false) {
		return;
	}

	filesToMove = removeDuplicate(filesToMove);

	length = filesToMove.length;
	for (i = 0; i < length; i++) {
		file = filesToMove[i];
		fs.copy(file.from, path.join(file.to, file.rename), function(err) {
			if (err) {
				return console.error(err);
			}
		});
	}
}

function generateFileList() {
	var filesToMove = [], libFolder = '', libName = '', file, fileNameAndExt, fileName, fileFolder,
		extension, temp, currLib, length, i, asteriskName;

	for (var lib in source) {
		if (source.hasOwnProperty(lib)) {
			temp = lib.split('#');
			libName = temp[0];
			libFolder = temp[1] || '';
			currLib = source[lib];

			// For each files to get in the lib
			for (file in currLib) {
				if (currLib.hasOwnProperty(file)) {
					temp = file.split('#');
					fileNameAndExt = temp[0] || '*';
					fileName = path.basename(fileNameAndExt, path.extname(fileNameAndExt));
					fileFolder = temp[1] || '';
					extension = path.extname(fileNameAndExt).substr(1);

					// Because of the rename function and the folder specification, the Globstar" ** matching of glob can't be use
					if (containDoubleAsterisk.test(currLib[file])) {
						console.error(colors.red('The "Globstar" ** matching wan\'t support by CLEAN-BOWER-INSTALLER. You have to specify each folder and their destination if you really need it.'));
						console.error(colors.red('Please correct the source: '+ libName));
						return false;
					}

					// List files who fit the
					var files = glob.sync(path.join(bowerFolder, libName, currLib[file]));

					// Add the needed folders to the Array filesToMove
					length = files.length;
					asteriskName = false;
					for (i = 0; i < length; i++) {
						if (containAsterisk.test(fileName)) {
							asteriskName = true;
						}

						if (asteriskName) {
							fileName = path.basename(files[i], path.extname(files[i]));
						}

						// Test if the link is global or relative
						if (startWithSlash.test(fileFolder)) {
							// The specified file folder is global
							filesToMove.push({
								'from': files[i],
								'to': path.join(bowerPath, fileFolder.substr(1)),
								'rename': fileName + path.extname(files[i])
							});
						} else if (startWithSlash.test(libFolder)) {
							// The specified lib folder is global
							filesToMove.push({
								'from': files[i],
								'to': path.join(bowerPath, libFolder.substr(1), fileFolder),
								'rename': fileName + path.extname(files[i])
							});
						} else {
							// None of the file or lib specified folder is global
							filesToMove.push({
								'from': files[i],
								'to': path.join(bowerPath, option.default, folder[path.extname(files[i]).substr(1)], libFolder, fileFolder),
								'rename': fileName + path.extname(files[i])
							});
						}
					}
				}
			}
		}
	}

	return filesToMove;
}

/**
 * Remove duplicate input in a array and short it
 *
 * @param a {Array}
 * @returns {Array}
 */
function removeDuplicate(a) {
	return a.sort().filter(function(item, pos) {
		return !pos || item != a[pos - 1];
	})
}

/* API commands*/
/**
 * API command to run clean-bower-install with a bower.json in the same folder
 *
 * @param [command] {string}
 */
function run(command) {
	runAsCli = false;

	if (verify(command) !== false) {
		if (command != 'bowerPath') {
			execute(command);
		}
	}

	if (command === undefined) {
		runCBI();
	}
}

/**
 * API command to run clean-bower-install with a bower.json in a specified folder
 *
 * @param relativePath {string}
 * @param [command] {string}
 */
function runFrom(relativePath, command) {
	runAsCli = false;

	var bowerFilePath = path.join(__dirname, relativePath);

	// Error handling
	if (relativePath === undefined) {
		throw new Error('Wrong call to clean-bower-install runFrom() function: No path provided.');
	}

	if (!testIfPathExist(bowerFilePath)) {
		throw new Error('Can not find the file bower.json at the specified path.');
	}

	// Execution
	bowerPath = bowerFilePath;

	if (verify(command) !== false) {
		if (command != 'bowerPath') {
			execute(command);
		}
	}

	if (command === undefined) {
		runCBI();
	}
}

exports.run = run;
exports.runFrom = runFrom;<|endoftext|>


<commit_before>require('chai').should();
var Model = require('../src/model.js');

describe('Model', function() {
  var Todo;

  it('allows the definition of data models', function() {
    Todo = Model('todos', {
      title: 'Untitled',
      done: false,

      computed: function() {
        return this.get('title');
      },

      check: function() {
        this.set('done', true);
      }
    });
  });

  describe('#create()', function() {
    it('allows the creation of model instances', function() {
      Todo.create();
    });
  });

  describe.skip('#load()', function() {
    it('allows the loading of model instances', function(done) {
      var todo = Todo.load(done);
    });
  });

  describe('Instance', function() {

    describe('#get()', function() {
      it('returns default values', function() {
        var todo = Todo.create();
        todo.get('title').should.equal('Untitled');
      });

      it('returns the values passed to the constructor', function() {
        var todo = Todo.create({title: 'Foo'});
        todo.get('title').should.equal('Foo');
      });

      it('returns previously set values', function() {
        Todo.create().set({a: 'b', b: 'a'}).get('b').should.equal('a');
      });

      it('makes computed properties from model methods', function(done) {
        var todo = Todo.create();
        todo.get('computed');
        todo.on('change:computed', function() {
          todo.get('computed').should.equal('Foo');
          done();
        });
        todo.set('title', 'Foo');
      });
    });

    it.skip('saves without error', function(done) {
      Todo.create().save(done);
    });

    it('should be dirty when fresh', function() {
      Todo.create().isDirty().should.equal(true);
    });

    it.skip('emits a generic change event when a property is set', function(done) {
      var todo = Todo.create();
      todo.on('change', function() {
        todo.get('done').should.equal(true);
        done();
      });
      todo.check();
    });

    it.skip('emits a specific change event when a property is set', function(done) {
      var todo = Todo.create();
      todo.on('change:done', function() {
        todo.get('done').should.equal(true);
        done();
      });
      todo.check();
    });
  });
});
<commit_msg>Update test suite to match behaviour change<commit_after>require('chai').should();
var Model = require('../src/model.js');

describe('Model', function() {
  var Todo;

  it('allows the definition of data models', function() {
    Todo = Model('todos', {
      title: 'Untitled',
      done: false,

      computed: function() {
        return this.get('title');
      },

      check: function() {
        this.set('done', true);
      }
    });
  });

  describe('#create()', function() {
    it('allows the creation of model instances', function() {
      Todo.create();
    });
  });

  describe.skip('#load()', function() {
    it('allows the loading of model instances', function(done) {
      var todo = Todo.load(done);
    });
  });

  describe('Instance', function() {

    describe('#get()', function() {
      it('returns default values', function() {
        var todo = Todo.create();
        todo.get('title').should.equal('Untitled');
      });

      it('returns the values passed to the constructor', function() {
        var todo = Todo.create({title: 'Foo'});
        todo.get('title').should.equal('Foo');
      });

      it('returns previously set values', function() {
        Todo.create().set({a: 'b', b: 'a'}).get('b').should.equal('a');
      });

      it('makes computed properties from model methods', function(done) {
        var todo = Todo.create();
        todo.get('computed');
        todo.on('change:computed', function() {
          todo.get('computed').should.equal('Foo');
          done();
        });
        todo.set('title', 'Foo');
      });
    });

    it.skip('saves without error', function(done) {
      Todo.create().save(done);
    });

    it('should not be dirty when fresh', function() {
      Todo.create().isDirty().should.equal(false);
    });

    it.skip('emits a generic change event when a property is set', function(done) {
      var todo = Todo.create();
      todo.on('change', function() {
        todo.get('done').should.equal(true);
        done();
      });
      todo.check();
    });

    it.skip('emits a specific change event when a property is set', function(done) {
      var todo = Todo.create();
      todo.on('change:done', function() {
        todo.get('done').should.equal(true);
        done();
      });
      todo.check();
    });
  });
});
<|endoftext|>


<commit_before>
/**
 * Test dependencies.
 */

var mouth = require('..');
var assert = require('assert');

describe("substitution", function() {

	var tmpl;
	beforeEach(function() {
		tmpl = mouth({
			beep: 'boop',
			foo: 'bar'
		});
	});

	it("should substitue a single variable", function() {
		var result = tmpl('${ beep }');
		assert.equal(result, 'boop');
	});

	it("should substitue multiple variables", function() {
		var result = tmpl('${ beep } and ${ foo }');
		assert.equal(result, 'boop and bar');
	});

	it('should be dead simple', function() {
		assert.equal(mouth({
			hello: 'world'
		}, 'hello ${hello}!'), 'hello world!');
	});
});

describe("plugins", function() {

	var tmpl;
	beforeEach(function() {
		tmpl = mouth({
			link: 'home'
		});
	});

	it("should add middlware", function() {
		tmpl.use('path', function(str) {
			return '/path/' + this[str.trim()];
		});
		var result = tmpl('$path{link}');
		assert.equal(result, '/path/home');
	});

});

describe("filters", function() {

});

<commit_msg>start refactoring new api<commit_after>
/**
 * Test dependencies.
 */

var mouth = require('..');
var assert = require('assert');

describe('substitution', function() {

	it("should substitue a single variable", function() {
		var tmpl = mouth('hello ${name}');
		var result = tmpl({
			name: 'boop'
		});

		assert.equal(result, 'hello boop');
	});

	it("should substitue multiple variables", function() {
		var tmpl = mouth('${ beep } and ${ foo }');
		var result = tmpl({
			beep: 'boop',
			foo: 'bar'
		});

		assert.equal(result, 'boop and bar');
	});

	it('should be dead simple', function() {
		assert.equal(mouth('hello ${hello}!', {
			hello: 'world'
		}), 'hello world!');
	});

});

// describe("substitution", function() {

// 	var tmpl;
// 	beforeEach(function() {
// 		tmpl = mouth({
// 			beep: 'boop',
// 			foo: 'bar'
// 		});
// 	});

// 	it("should substitue a single variable", function() {
// 		var result = tmpl('${ beep }');
// 		assert.equal(result, 'boop');
// 	});

// 	it("should substitue multiple variables", function() {
// 		var result = tmpl('${ beep } and ${ foo }');
// 		assert.equal(result, 'boop and bar');
// 	});

// 	it('should be dead simple', function() {
// 		assert.equal(mouth({
// 			hello: 'world'
// 		}, 'hello ${hello}!'), 'hello world!');
// 	});
// });

// describe("mixins", function() {

// 	var tmpl;
// 	beforeEach(function() {
// 		tmpl = mouth({
// 			link: 'home'
// 		});
// 	});

// 	it("should add middlware", function() {
// 		tmpl.use('path', function(str) {
// 			return '/path/' + this[str.trim()];
// 		});
// 		var result = tmpl('$path{link}');
// 		assert.equal(result, '/path/home');
// 	});

// });

describe("filters", function() {

});

<|endoftext|>


<commit_before>'use strict';

var toposort = require('toposort');

/*
  Sorts dependencies between chunks by their "parents" attribute.

  This function sorts chunks based on their dependencies with each other.
  The parent relation between chunks as generated by Webpack for each chunk
  is used to define a directed (and hopefully acyclic) graph, which is then
  topologically sorted in order to retrieve the correct order in which
  chunks need to be embedded into HTML. A directed edge in this graph is
  describing a "is parent of" relationship from a chunk to another (distinct)
  chunk. Thus topological sorting orders chunks from bottom-layer chunks to
  highest level chunks that use the lower-level chunks.

  @param {Array} chunks an array of chunks as generated by the html-webpack-plugin.
  It is assumed that each entry contains at least the properties "id"
  (containing the chunk id) and "parents" (array containing the ids of the
  parent chunks).

  @return {Array} A topologically sorted version of the input chunks
*/
module.exports.dependency = function (chunks) {
  if (!chunks) {
    return chunks;
  }

  // We build a map (chunk-id -> chunk) for faster access during graph building.
  var nodeMap = {};

  chunks.forEach(function (chunk) {
    nodeMap[chunk.id] = chunk;
  });

  // Next, we add an edge for each parent relationship into the graph
  var edges = [];

  chunks.forEach(function (chunk) {
    if (chunk.parents) {
      // Add an edge for each parent (parent -> child)
      chunk.parents.forEach(function (parentId) {
        var parentChunk = nodeMap[parentId];
        if (!parentChunk) {
          throw new Error('Can not find chunk parent during dependency sort');
        }
        edges.push([parentChunk, chunk]);
      });
    }
  });
  // We now perform a topological sorting on the input chunks and built edges
  return toposort.array(chunks, edges);
};

/**
 * Sorts the chunks based on the chunk id.
 *
 * @param  {Array} chunks the list of chunks to sort
 * @return {Array} The sorted list of chunks
 */
module.exports.id = function (chunks) {
  return chunks.sort(function orderEntryLast (a, b) {
    if (a.entry !== b.entry) {
      return b.entry ? 1 : -1;
    } else {
      return b.id - a.id;
    }
  });
};

/**
 * Performs identity mapping (no-sort).
 * @param  {Array} chunks the chunks to sort
 * @return {Array} The sorted chunks
 */
module.exports.none = function (chunks) {
  return chunks;
};

/**
 * Defines the default sorter.
 */
module.exports.auto = module.exports.id;
<commit_msg>Fix sorting for webpack2<commit_after>'use strict';

var toposort = require('toposort');

/*
  Sorts dependencies between chunks by their "parents" attribute.

  This function sorts chunks based on their dependencies with each other.
  The parent relation between chunks as generated by Webpack for each chunk
  is used to define a directed (and hopefully acyclic) graph, which is then
  topologically sorted in order to retrieve the correct order in which
  chunks need to be embedded into HTML. A directed edge in this graph is
  describing a "is parent of" relationship from a chunk to another (distinct)
  chunk. Thus topological sorting orders chunks from bottom-layer chunks to
  highest level chunks that use the lower-level chunks.

  @param {Array} chunks an array of chunks as generated by the html-webpack-plugin.
  It is assumed that each entry contains at least the properties "id"
  (containing the chunk id) and "parents" (array containing the ids of the
  parent chunks).

  @return {Array} A topologically sorted version of the input chunks
*/
module.exports.dependency = function (chunks) {
  if (!chunks) {
    return chunks;
  }

  // We build a map (chunk-id -> chunk) for faster access during graph building.
  var nodeMap = {};

  chunks.forEach(function (chunk) {
    nodeMap[chunk.id] = chunk;
  });

  // Next, we add an edge for each parent relationship into the graph
  var edges = [];

  chunks.forEach(function (chunk) {
    if (chunk.parents) {
      // Add an edge for each parent (parent -> child)
      chunk.parents.forEach(function (parentId) {
        var parentChunk = nodeMap[parentId];
        if (!parentChunk) {
          throw new Error('Can not find chunk parent during dependency sort');
        }
        edges.push([parentChunk, chunk]);
      });
    }
  });
  // We now perform a topological sorting on the input chunks and built edges
  return toposort.array(chunks, edges);
};

/**
 * Sorts the chunks based on the chunk id.
 *
 * @param  {Array} chunks the list of chunks to sort
 * @return {Array} The sorted list of chunks
 */
module.exports.id = function (chunks) {
  return chunks.sort(function orderEntryLast (a, b) {
    if (a.entry !== b.entry) {
      return b.entry ? 1 : -1;
    } else {
      return b.id - a.id;
    }
  });
};

/**
 * Performs identity mapping (no-sort).
 * @param  {Array} chunks the chunks to sort
 * @return {Array} The sorted chunks
 */
module.exports.none = function (chunks) {
  return chunks;
};

/**
 * Defines the default sorter.
 */
module.exports.auto = module.exports.id;

// In webpack 2 the ids are fliped around.
// Therefore the id sort is broken
// Luckily the dependency sort is working as expected
if (require('webpack/package.json').version.split('.')[0] === '2') {
  module.exports.auto = module.exports.dependency;
}
<|endoftext|>


<commit_before>
var Counter = Backbone.Model.extend({
    defaults : {"value" : 0}
});

var CounterView = Backbone.View.extend({
    render: function () {
        var val = this.model.get("value");
        var btn = '<button id="btn">Increment</button>';
        var btn2 = '<button id = "btn2">Decrease</button>';
        var clr = '<button id = "clr">Clear</button>';
        this.$el.html('
'+val+'
' + btn + btn2 + clr);
    }
});

var counterModel;
var counterView;
$(document).ready( function () {

    counterModel = new Counter();

    counterView = new CounterView({model : counterModel});
    counterView.render();

    counterModel.on("change", function () {
        counterView.render();
    });

    counterView.$el.on("click","#btn", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        mod.set("value",currVal+1);
    });

    counterView.$el.on("click","#btn2", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        mod.set("value",currVal-1);
    });

    counterView.$el.on("click","#clr", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        mod.set("value",currVal = 0);
    });

    $("#counterdiv").append(counterView.$el);

});
<commit_msg>stopped decrease counter when currVal is 0<commit_after>
var Counter = Backbone.Model.extend({
    defaults : {"value" : 0}
});

var CounterView = Backbone.View.extend({
    render: function () {
        var val = this.model.get("value");
        var btn = '<button id="btn">Increment</button>';
        var btn2 = '<button id = "btn2">Decrease</button>';
        var clr = '<button id = "clr">Clear</button>';
        this.$el.html('
'+val+'
' + btn + btn2 + clr);
    }
});

var counterModel;
var counterView;
$(document).ready( function () {

    counterModel = new Counter();

    counterView = new CounterView({model : counterModel});
    counterView.render();

    counterModel.on("change", function () {
        counterView.render();
    });

    counterView.$el.on("click","#btn", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        mod.set("value",currVal+1);
    });

    counterView.$el.on("click","#btn2", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        //mod.set("value")
          if (currVal === 0) {
            return
          } else {
            mod.set("value",currVal-1);
            }
    });

    counterView.$el.on("click","#clr", function () {
        var mod = counterView.model;
        var currVal = mod.get("value");
        mod.set("value",currVal = 0);
    });

    $("#counterdiv").append(counterView.$el);

});
<|endoftext|>


<commit_before>/**
 * Created with RubyMine.
 * User: teamco
 * Date: 4/6/15
 * Time: 7:43 PM
 */

define(function definePageWidgetCopy() {

    /**
     * Define PageWidgetCopy
     * @class PageWidgetCopy
     * @constructor
     */
    var PageWidgetCopy = function PageWidgetCopy() {
    };

    return PageWidgetCopy.extend(
        'PageWidgetCopy', {

            /**
             * Define clone widgets from other page
             * @member PageWidgetCopy
             * @param {Page} fromPage
             */
            cloneWidgets: function cloneWidgets(fromPage) {

                /**
                 * Get clone page items
                 * @type {{Widget: {}}}
                 */
                var cloneWidgets = fromPage.model.getItems(),
                    index, cloneMap = {};

                for (index in cloneWidgets) {

                    if (cloneWidgets.hasOwnProperty(index)) {

                        /**
                         * Get updated clone map
                         * @type {Object}
                         */
                        cloneMap = this.cloneWidget(
                            cloneWidgets[index], cloneMap
                        );
                    }
                }

                this.defineWidgetsRules(cloneMap);
            },

            /**
             * Define clone widget
             * @member PageWidgetCopy
             * @param {Widget} cloneWidget
             * @param {Object} cloneMap
             * @returns {Object}
             */
            cloneWidget: function cloneWidget(cloneWidget, cloneMap) {

                this.scope.logger.debug('Clone widget', arguments);

                // Get prefs
                var cloneWidgetPrefs = cloneWidget.model.getConfig('preferences');

                if (typeof(cloneWidgetPrefs.resource) === 'undefined') {

                    cloneWidget.logger.warn('Undefined resource', cloneWidgetPrefs);
                    return false;
                }

                // Create without render
                this.createWidgetFromResource({

                    resource: cloneWidgetPrefs.resource,
                    thumbnail: cloneWidgetPrefs.thumbnail,
                    title: cloneWidgetPrefs.title,
                    description: cloneWidgetPrefs.description,
                    width: cloneWidget.dom.width,
                    height: cloneWidget.dom.height

                }, false, true);

                /**
                 * Get current widget
                 * @type {Widget}
                 */
                var currentWidget = this.model.getCurrentItem(),
                    key;

                // Define map
                cloneMap[cloneWidget.model.getUUID()] = currentWidget.model.getUUID();

                // Copy dom
                currentWidget.dom = cloneWidget.dom;

                // Render widget
                currentWidget.observer.publish(
                    currentWidget.eventmanager.eventList.successRendered
                );

                for (key in cloneWidgetPrefs) {

                    if (cloneWidgetPrefs.hasOwnProperty(key)) {

                        currentWidget.config.preferences[key] =
                            cloneWidgetPrefs[key];
                    }
                }

                // Temporary clone rules
                currentWidget.config.rules = cloneWidget.model.getConfig('rules');

                return cloneMap;
            },

            /**
             * Define widget rules
             * @member PageWidgetCopy
             * @param {Object} cloneMap
             */
            defineWidgetsRules: function defineWidgetsRules(cloneMap) {

                // Get all page widgets
                var items = this.model.getItems();

                for (var item in items) {

                    if (items.hasOwnProperty(item)) {

                        /**
                         * Get widget
                         * @type {Widget}
                         */
                        currentWidget = items[item];

                        this.defineWidgetRules(currentWidget, cloneMap);
                    }
                }
            },

            /**
             * Define widget rules
             * @member PageWidgetCopy
             * @param {Widget} currentWidget
             * @param {Array} cloneMap
             */
            defineWidgetRules: function defineWidgetRules(currentWidget, cloneMap) {

                this.scope.logger.debug('Define widget rules', arguments);

                this._copyWidgetRulesSubscribe(currentWidget, cloneMap);
                this._copyWidgetRulesSubscribers(currentWidget, cloneMap);
            },

            /**
             * Define copy widget rules subscribe
             * @member PageWidgetCopy
             * @param {Widget} widget
             * @param {Object} cloneMap
             * @private
             */
            _copyWidgetRulesSubscribe: function _copyWidgetRulesSubscribe(widget, cloneMap) {

                // Get widget rules
                var rules = widget.model.getConfig('rules');

                /**
                 * Get subscribed widgets
                 * @type {Object}
                 */
                var subscribe = rules.subscribe || {},
                    rs, currentKey, z = 0;

                if (typeof(subscribe) === 'undefined') {

                    this.scope.logger.debug('Undefined subscribe', rules);
                    return false;
                }

                for (rs in subscribe) {

                    if (subscribe.hasOwnProperty(rs)) {

                        // Get current key
                        currentKey = cloneMap[rs];

                        if (rs.match(/content/)) {

                            // Get content key
                            currentKey = cloneMap[rs.replace(/\-content/, '')] + '-content';
                        }

                        subscribe[currentKey] = {};

                        for (var sk in subscribe[rs]) {

                            if (subscribe[rs].hasOwnProperty(sk)) {

                                // Define subscribe array
                                subscribe[currentKey][sk] = subscribe[currentKey][sk] || [];

                                for (; z < subscribe[rs][sk].length; z++) {

                                    // Fill subscribe
                                    subscribe[currentKey][sk].push(
                                        subscribe[rs][sk][z]
                                    );
                                }
                            }
                        }

                        // Delete temp rules
                        delete subscribe[rs];
                    }
                }
            },

            /**
             * Define copy widget rules subscribers
             * @member PageWidgetCopy
             * @param {Widget} widget
             * @param {Object} cloneMap
             * @private
             */
            _copyWidgetRulesSubscribers: function _copyWidgetRulesSubscribers(widget, cloneMap) {

                // Get widget rules
                var rules = widget.model.getConfig('rules');

                /**
                 * Get widget subscribers
                 * @type {Object}
                 */
                var subscribers = rules.subscribers || {},
                    rs, ssk = 0;

                for (rs in subscribers) {

                    if (subscribers.hasOwnProperty(rs)) {

                        // Get subscribers array of items
                        var subscribersItems = subscribers[rs] || [];

                        for (; ssk < subscribersItems.length; ssk++) {

                            // Set cloned key
                            subscribersItems[ssk] = cloneMap[subscribersItems[ssk]];
                        }
                    }
                }
            }
        }
    );
});<commit_msg>fix page clone (rules)<commit_after>/**
 * Created with RubyMine.
 * User: teamco
 * Date: 4/6/15
 * Time: 7:43 PM
 */

define(function definePageWidgetCopy() {

    /**
     * Define PageWidgetCopy
     * @class PageWidgetCopy
     * @constructor
     */
    var PageWidgetCopy = function PageWidgetCopy() {
    };

    return PageWidgetCopy.extend(
        'PageWidgetCopy', {

            /**
             * Define clone widgets from other page
             * @member PageWidgetCopy
             * @param {Page} fromPage
             */
            cloneWidgets: function cloneWidgets(fromPage) {

                /**
                 * Get clone page items
                 * @type {{Widget: {}}}
                 */
                var cloneWidgets = fromPage.model.getItems(),
                    index, cloneMap = {};

                for (index in cloneWidgets) {

                    if (cloneWidgets.hasOwnProperty(index)) {

                        /**
                         * Get updated clone map
                         * @type {Object}
                         */
                        cloneMap = this.cloneWidget(
                            cloneWidgets[index], cloneMap
                        );
                    }
                }

                this.defineWidgetsRules(cloneMap);
            },

            /**
             * Define clone widget
             * @member PageWidgetCopy
             * @param {Widget} cloneWidget
             * @param {Object} cloneMap
             * @returns {Object}
             */
            cloneWidget: function cloneWidget(cloneWidget, cloneMap) {

                this.scope.logger.debug('Clone widget', arguments);

                // Get prefs
                var cloneWidgetPrefs = cloneWidget.model.getConfig('preferences');

                if (typeof(cloneWidgetPrefs.resource) === 'undefined') {

                    cloneWidget.logger.warn('Undefined resource', cloneWidgetPrefs);
                    return false;
                }

                // Create without render
                this.createWidgetFromResource({

                    resource: cloneWidgetPrefs.resource,
                    thumbnail: cloneWidgetPrefs.thumbnail,
                    title: cloneWidgetPrefs.title,
                    description: cloneWidgetPrefs.description,
                    width: cloneWidget.dom.width,
                    height: cloneWidget.dom.height

                }, false, true);

                /**
                 * Get current widget
                 * @type {Widget}
                 */
                var currentWidget = this.model.getCurrentItem(),
                    key;

                // Define map
                cloneMap[cloneWidget.model.getUUID()] = currentWidget.model.getUUID();

                // Copy dom
                currentWidget.dom = cloneWidget.dom;

                // Render widget
                currentWidget.observer.publish(
                    currentWidget.eventmanager.eventList.successRendered
                );

                for (key in cloneWidgetPrefs) {

                    if (cloneWidgetPrefs.hasOwnProperty(key)) {

                        currentWidget.config.preferences[key] =
                            cloneWidgetPrefs[key];
                    }
                }

                // Temporary clone rules
                currentWidget.config.rules = cloneWidget.model.getConfig('rules');

                return cloneMap;
            },

            /**
             * Define widget rules
             * @member PageWidgetCopy
             * @param {Object} cloneMap
             */
            defineWidgetsRules: function defineWidgetsRules(cloneMap) {

                // Get all page widgets
                var items = this.model.getItems();

                for (var item in items) {

                    if (items.hasOwnProperty(item)) {

                        /**
                         * Get widget
                         * @type {Widget}
                         */
                        currentWidget = items[item];

                        this.defineWidgetRules(currentWidget, cloneMap);
                    }
                }
            },

            /**
             * Define widget rules
             * @member PageWidgetCopy
             * @param {Widget} currentWidget
             * @param {Array} cloneMap
             */
            defineWidgetRules: function defineWidgetRules(currentWidget, cloneMap) {

                this.scope.logger.debug('Define widget rules', arguments);

                this._copyWidgetRulesSubscribe(currentWidget, cloneMap);
                this._copyWidgetRulesSubscribers(currentWidget, cloneMap);
            },

            /**
             * Define copy widget rules subscribe
             * @member PageWidgetCopy
             * @param {Widget} widget
             * @param {Object} cloneMap
             * @private
             */
            _copyWidgetRulesSubscribe: function _copyWidgetRulesSubscribe(widget, cloneMap) {

                // Get widget rules
                var rules = widget.model.getConfig('rules');

                /**
                 * Get subscribed widgets
                 * @type {Object}
                 */
                var subscribe = rules.subscribe || {},
                    rs, currentKey, z = 0,
                    removeSubscribe = [];

                if (typeof(subscribe) === 'undefined') {

                    this.scope.logger.debug('Undefined subscribe', rules);
                    return false;
                }

                for (rs in subscribe) {

                    if (subscribe.hasOwnProperty(rs)) {

                        // Get current key
                        currentKey = cloneMap[rs];

                        if (rs.match(/content/)) {

                            // Get content key
                            currentKey = cloneMap[rs.replace(/\-content/, '')] + '-content';
                        }

                        subscribe[currentKey] = {};

                        for (var sk in subscribe[rs]) {

                            if (subscribe[rs].hasOwnProperty(sk)) {

                                // Define subscribe array
                                subscribe[currentKey][sk] = subscribe[currentKey][sk] || [];

                                for (; z < subscribe[rs][sk].length; z++) {

                                    // Fill subscribe
                                    subscribe[currentKey][sk].push(
                                        subscribe[rs][sk][z]
                                    );
                                }
                            }
                        }

                        // Collect temp rules
                        removeSubscribe.push(rs);
                    }
                }

                var sl = removeSubscribe.length;

                for (z = 0; z < sl; z++) {

                    // Delete temp rules
                    delete subscribe[removeSubscribe[z]];
                }
            },

            /**
             * Define copy widget rules subscribers
             * @member PageWidgetCopy
             * @param {Widget} widget
             * @param {Object} cloneMap
             * @private
             */
            _copyWidgetRulesSubscribers: function _copyWidgetRulesSubscribers(widget, cloneMap) {

                // Get widget rules
                var rules = widget.model.getConfig('rules');

                /**
                 * Get widget subscribers
                 * @type {Object}
                 */
                var subscribers = rules.subscribers || {},
                    rs, ssk = 0;

                for (rs in subscribers) {

                    if (subscribers.hasOwnProperty(rs)) {

                        // Get subscribers array of items
                        var subscribersItems = subscribers[rs] || [];

                        for (; ssk < subscribersItems.length; ssk++) {

                            // Set cloned key
                            subscribersItems[ssk] = cloneMap[subscribersItems[ssk]];
                        }
                    }
                }
            }
        }
    );
});<|endoftext|>


<commit_before>var Stream = require('./stream');
var Socket = require('./socket');
var utils = require('./utils');

// export event emitter object (clone before use!)
module.exports = function (emitter) {

    // merge flow emitter to existing object
    if (emitter) {

        for (var key in FlowEmitter) {
            emitter[key] = FlowEmitter[key];
        }

    // create a new emitter
    } else {
        emitter = utils.clone(FlowEmitter);
    }

    emitter._flows = {};
    return emitter;
};

var FlowEmitter = {

    /**
     * Call handlers of a specific event or pipe if handler is a stream.
     *
     * @public
     * @param {string} The event name.
     * @param {object} The data for the event handlers.
     */
    flow: function (eventName, context, stream) {
        var events = this._flows;

        // create streams
        stream = stream || Stream(this, context);

        // check if event exists
        if (events[eventName] && events[eventName].length) {

            // index for events that must be removed
            var obsoleteEvents = [];
            var i, l;
            var config;

            // call handlers
            for (i = 0, l = events[eventName].length; i < l; ++i) {
                if ((config = events[eventName][i])) {

                    // pass stream to flow to setup handlers
                    Flow.call(this, stream, context, config);

                    // remove from event buffer, if once is true
                    if (config['1']) {
                        config = undefined;
                        obsoleteEvents.push([eventName, i]);
                    }
                }
            }

            // remove obsolete events
            if (obsoleteEvents.length) {
                for (i = 0, l = obsoleteEvents.length; i < l; ++i) {

                    // remove handler
                    events[obsoleteEvents[i][0]].splice(obsoleteEvents[i][1], 1);

                    // remove event
                    if (events[obsoleteEvents[i][0]].length === 0) {
                        delete events[obsoleteEvents[i][0]];
                    }
                }
            }
        }

        // return transmitter to send and receive data
        return stream;
    },

    /**
     * Mind an an event.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {object} The flow handler config.
     */
    mind: function (eventName, config) {

        var events = this._flows;

        if (!events[eventName]) {
            events[eventName] = [];
        }

        events[eventName].push(config);

        return this;
    }
};

/**
 * Create a new config event handler.
 *
 * @public
 * @param {object} The module instance.
 * @param {object} The "out" config.
 * @param {object} The adapter object (optional).
 */
function Flow (originStream, context, config) {
    var instance = this;
    var session = originStream.session;
    var role = (session || {})[engine.session_role];

    // handle "to" config
    if (typeof config.to === 'string') {

        if (!(instance = engine.instances[config.to])) {

            // pause originStream
            originStream.pause();

            return engine.load(config.to, role, function (err, instance) {

                if (err) {
                    return originStream.write(err).resume();
                }

                // create and setup target stream
                createAndSetupStream(instance, originStream, context, config);

                // resume orign stream
                originStream.resume();
            });
        }

        // check access
        if (session && !utils.roleAccess(instance, role)) {
            return originStream.write(new Error('Flow target instance "' + config.to + '" is not found.'));
        }
    }

    createAndSetupStream(instance, originStream, context, config);
}

function createAndSetupStream (instance, originStream, context, config) {

    // create a duplex stream for every event
    var stream = Stream(instance, context);

    // pipe duplex streams in both directions
    stream.pipe(originStream).pipe(stream);

    // add data handler to stream
    if (
        config.data instanceof Array &&
        (config.data[0] = getMethodFromPath(config.data[0], instance))
    ) {
        stream.data(config.data[0], config.data[1]);
    }

    // emit stream to other flow handlers
    if (typeof config.emit === 'string') {
        instance.flow(config.emit, null, stream);
    }

    // pass stream to receiver function
    if (config.call) {

        // call scket stream handler
        if (typeof config.call === 'string' && config.call.indexOf('/') > -1) {
            Socket.stream(stream, config.call);

        // call local method
        } else if ((config.call = getMethodFromPath(config.call, instance))) {
            config.call.call(instance, stream, config);
        }
    }
}

/**
 * Return a function or undefined.
 */
function getMethodFromPath (path, module_instance) {

    if (typeof path === 'function') {
        return path;
    }
    if (
        typeof path === 'string' &&
        typeof (path = utils.path(path, [engine.handlers, module_instance, global])) !== 'function'
    ) {
        console.error('Flow method is not a function.');
        return;
    }

    return path;
}
<commit_msg>setup and return event stream without distributing<commit_after>var Stream = require('./stream');
var Socket = require('./socket');
var utils = require('./utils');

// export event emitter object (clone before use!)
module.exports = function (emitter) {

    // merge flow emitter to existing object
    if (emitter) {

        for (var key in FlowEmitter) {
            emitter[key] = FlowEmitter[key];
        }

    // create a new emitter
    } else {
        emitter = utils.clone(FlowEmitter);
    }

    emitter._flows = {};
    return emitter;
};

var FlowEmitter = {

    /**
     * Call handlers of a specific event or pipe if handler is a stream.
     *
     * @public
     * @param {string} The event name.
     * @param {object} The data for the event handlers.
     */
    flow: function (eventName, context, stream) {
        var events = this._flows;

        // create streams
        stream = stream || Stream(this, context);

        // setup event stream directly if eventName is an object
        if (eventName && eventName.on) {
            Flow.call(this, stream, eventName, context);
            return stream;
        }

        // check if event exists
        if (events[eventName] && events[eventName].length) {

            // index for events that must be removed
            var obsoleteEvents = [];
            var i, l;
            var config;

            // call handlers
            for (i = 0, l = events[eventName].length; i < l; ++i) {
                if ((config = events[eventName][i])) {

                    // pass stream to flow to setup handlers
                    Flow.call(this, stream, config, context);

                    // remove from event buffer, if once is true
                    if (config['1']) {
                        config = undefined;
                        obsoleteEvents.push([eventName, i]);
                    }
                }
            }

            // remove obsolete events
            if (obsoleteEvents.length) {
                for (i = 0, l = obsoleteEvents.length; i < l; ++i) {

                    // remove handler
                    events[obsoleteEvents[i][0]].splice(obsoleteEvents[i][1], 1);

                    // remove event
                    if (events[obsoleteEvents[i][0]].length === 0) {
                        delete events[obsoleteEvents[i][0]];
                    }
                }
            }
        }

        // return transmitter to send and receive data
        return stream;
    },

    /**
     * Mind an an event.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {object} The flow handler config.
     */
    mind: function (eventName, config) {

        var events = this._flows;

        if (!events[eventName]) {
            events[eventName] = [];
        }

        events[eventName].push(config);

        return this;
    }
};

/**
 * Create a new config event handler.
 *
 * @public
 * @param {object} The module instance.
 * @param {object} The "out" config.
 * @param {object} The adapter object (optional).
 */
function Flow (originStream, config, context) {
    var instance = this;
    var session = originStream.session;
    var role = (session || {})[engine.session_role];

    // handle "to" config
    if (typeof config.to === 'string') {

        if (!(instance = engine.instances[config.to])) {

            // pause originStream
            originStream.pause();

            return engine.load(config.to, role, function (err, instance) {

                if (err) {
                    return originStream.write(err).resume();
                }

                // create and setup target stream
                createAndSetupStream(instance, originStream, config, context);

                // resume orign stream
                originStream.resume();
            });
        }

        // check access
        if (session && !utils.roleAccess(instance, role)) {
            return originStream.write(new Error('Flow target instance "' + config.to + '" is not found.'));
        }
    }

    createAndSetupStream(instance, originStream, config, context);
}

function createAndSetupStream (instance, originStream, config, context) {

    // create a duplex stream for every event
    var stream = Stream(instance, context);

    // pipe duplex streams in both directions
    stream.pipe(originStream).pipe(stream);

    // add data handler to stream
    if (
        config.data instanceof Array &&
        (config.data[0] = getMethodFromPath(config.data[0], instance))
    ) {
        stream.data(config.data[0], config.data[1]);
    }

    // emit stream to other flow handlers
    if (typeof config.emit === 'string') {
        instance.flow(config.emit, null, stream);
    }

    // pass stream to receiver function
    if (config.call) {

        // call scket stream handler
        if (typeof config.call === 'string' && config.call.indexOf('/') > -1) {
            Socket.stream(stream, config.call);

        // call local method
        } else if ((config.call = getMethodFromPath(config.call, instance))) {
            config.call.call(instance, stream, config);
        }
    }
}

/**
 * Return a function or undefined.
 */
function getMethodFromPath (path, module_instance) {

    if (typeof path === 'function') {
        return path;
    }
    if (
        typeof path === 'string' &&
        typeof (path = utils.path(path, [engine.handlers, module_instance, global])) !== 'function'
    ) {
        console.error('Flow method is not a function.');
        return;
    }

    return path;
}
<|endoftext|>


<commit_before>// Copyright 2012 Mark Cavage, Inc.  All rights reserved.

var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var pooling = require('pooling');

var ConnectionError = require('../errors').ConnectionError;
var BindResponse = require('../messages').BindResponse;

var Client = require('./client');

///--- Globals

var STD_OPS = [
  'add',
  'del',
  'modify',
  'modifyDN'
];

var RETURN_VAL_OPS = [
  'compare',
  'exop'
];

///--- Internal Functions

function createPool(options, clientpool) {
  assert.ok(options);

  return pooling.createPool({
    checkInterval: options.checkInterval,
    log: options.log,
    name: 'ldapjs_' + (options.url || options.socketPath).replace(/[:/]/g, '_'),
    max: options.maxConnections,
    maxIdleTime: options.maxIdleTime,

    create: function createClient(callback) {
      var client = new Client(options);

      client.on('error', function (err) {
        client.removeAllListeners('connect');
        client.removeAllListeners('connectTimeout');
        clientpool.emit('error', err);
        return callback(err);
      });

      client.on('connectTimeout', function () {
        client.removeAllListeners('connect');
        clientpool.emit('connectTimeout');
      });

      client.once('connect', function onConnect() {
        client.removeAllListeners('error');

        if (!options.bindDN || !options.bindCredentials)
          return callback(null, client);

        function bindCallback(err, res) {
          if (err)
            return callback(err, null);

          return callback(null, client);
        }

        return client.bind(options.bindDN,
                           options.bindCredentials,
                           options.bindControls || [],
                           bindCallback);
      });
    },

    check: function check(client, callback) {
      // just do a root dse search
      client.search('', '(objectclass=*)', function (err, res) {
        if (err)
          return callback(err);

        res.on('error', function (e) {
          res.removeAllListeners('end');
          callback(e);
        });

        res.on('end', function () {
          res.removeAllListeners('error');
          callback(null);
        });

        return undefined;
      });
    },

    destroy: function destroy(client) {
      client.unbind(function () {});
    }
  });
}

///--- API

function ClientPool(options) {
  assert.ok(options);
  EventEmitter.call(this, options);

  this.log = options.log.child({clazz: 'ClientPool'}, true);
  this.options = {
    bindDN: options.bindDN,
    bindCredentials: options.bindCredentials,
    bindControls: options.bindControls || [],
    checkInterval: options.checkInterval,
    connectTimeout: (options.connectTimeout || 0),
    maxIdleTime: options.maxIdleTime,
    maxConnections: options.maxConnections,
    log: options.log,
    socketPath: options.socketPath,
    timeout: (options.timeout || 0),
    url: options.url,
    tlsOptions: options.tlsOptions
  };
  this.pool = createPool(options, this);
}
util.inherits(ClientPool, EventEmitter);
module.exports = ClientPool;

STD_OPS.forEach(function (op) {
  ClientPool.prototype[op] = function clientProxy() {
    var args = Array.prototype.slice.call(arguments);
    var cb = args.pop();
    if (typeof (cb) !== 'function')
      throw new TypeError('callback (Function) required');
    var self = this;

    return this.pool.acquire(function onAcquire(err, client) {
      if (err)
        return cb(err);

      args.push(function proxyCallback(err, res) {
        self.pool.release(client);
        return cb(err, res);
      });

      try {
        return Client.prototype[op].apply(client, args);
      } catch (e) {
        self.pool.release(client);
        return cb(e);
      }
    });
  };
});

RETURN_VAL_OPS.forEach(function (op) {
  ClientPool.prototype[op] = function clientProxy() {
    var args = Array.prototype.slice.call(arguments);
    var cb = args.pop();
    if (typeof (cb) !== 'function')
      throw new TypeError('callback (Function) required');
    var self = this;

    return this.pool.acquire(function onAcquire(poolErr, client) {
      if (poolErr)
        return cb(poolErr);

      args.push(function proxyCallback(err, val, res) {
        self.pool.release(client);
        return cb(err, val, res);
      });

      try {
        return Client.prototype[op].apply(client, args);
      } catch (e) {
        self.pool.release(client);
        return cb(e);
      }
    });
  };
});

ClientPool.prototype.search = function search(base, opts, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  var self = this;

  return this.pool.acquire(function onAcquire(err, client) {
    if (err)
      return callback(err);

    // This is largely in existence for search requests
    client.timeout = self.timeout || client.timeout;

    return client.search(base, opts, controls, function (err, res) {
      function cleanup() {
        self.pool.release(client);
      }

      if (err) {
        cleanup();
        return callback(err, res);
      }
      res.on('error', cleanup);
      res.on('end', cleanup);

      return callback(null, res);
    });
  });
};

ClientPool.prototype.abandon = function abandon(msgid, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  this.log.error({
    messageID: msgid
  }, 'Abandon is not supported with connection pooling. Ignoring.');
  return callback(null);
};

ClientPool.prototype.bind = function bind(dn, creds, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  var self = this;

  self.options.bindDN = null;
  self.options.bindCredentials = null;
  self.options.bindControls = null;

  return this.pool.shutdown(function () {
    self.pool = createPool(self.options, self);

    return self.pool.acquire(function onAcquire(err, client) {
      if (err)
        return callback(err);

      return client.bind(dn, creds, controls, function (err, res) {
        self.pool.release(client);

        if (err)
          return callback(err, res);

        self.options.bindDN = dn;
        self.options.bindCredentials = creds;
        self.options.bindControls = controls;

        return callback(null, res);
      });
    });
  });
};

ClientPool.prototype.unbind = function unbind(callback) {
  return this.pool.shutdown(callback);
};
<commit_msg>Fix error event handling for pooled clients<commit_after>// Copyright 2012 Mark Cavage, Inc.  All rights reserved.

var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var pooling = require('pooling');

var ConnectionError = require('../errors').ConnectionError;
var BindResponse = require('../messages').BindResponse;

var Client = require('./client');

///--- Globals

var STD_OPS = [
  'add',
  'del',
  'modify',
  'modifyDN'
];

var RETURN_VAL_OPS = [
  'compare',
  'exop'
];

///--- Internal Functions

function createPool(options, clientpool) {
  assert.ok(options);

  return pooling.createPool({
    checkInterval: options.checkInterval,
    log: options.log,
    name: 'ldapjs_' + (options.url || options.socketPath).replace(/[:/]/g, '_'),
    max: options.maxConnections,
    maxIdleTime: options.maxIdleTime,

    create: function createClient(callback) {
      var client = new Client(options);

      client.on('error', function (err) {
        client.removeAllListeners('connect');
        client.removeAllListeners('connectTimeout');
        if (clientpool.listeners('error').length) {
          clientpool.emit('error', err);
        }
        return callback(err);
      });

      client.on('connectTimeout', function () {
        client.removeAllListeners('connect');
        clientpool.emit('connectTimeout');
      });

      client.once('connect', function onConnect() {
        client.removeAllListeners('error');

        if (!options.bindDN || !options.bindCredentials)
          return callback(null, client);

        function bindCallback(err, res) {
          if (err)
            return callback(err, null);

          return callback(null, client);
        }

        return client.bind(options.bindDN,
                           options.bindCredentials,
                           options.bindControls || [],
                           bindCallback);
      });
    },

    check: function check(client, callback) {
      // just do a root dse search
      client.search('', '(objectclass=*)', function (err, res) {
        if (err)
          return callback(err);

        res.on('error', function (e) {
          res.removeAllListeners('end');
          callback(e);
        });

        res.on('end', function () {
          res.removeAllListeners('error');
          callback(null);
        });

        return undefined;
      });
    },

    destroy: function destroy(client) {
      client.unbind(function () {});
    }
  });
}

///--- API

function ClientPool(options) {
  assert.ok(options);
  EventEmitter.call(this, options);

  this.log = options.log.child({clazz: 'ClientPool'}, true);
  this.options = {
    bindDN: options.bindDN,
    bindCredentials: options.bindCredentials,
    bindControls: options.bindControls || [],
    checkInterval: options.checkInterval,
    connectTimeout: (options.connectTimeout || 0),
    maxIdleTime: options.maxIdleTime,
    maxConnections: options.maxConnections,
    log: options.log,
    socketPath: options.socketPath,
    timeout: (options.timeout || 0),
    url: options.url,
    tlsOptions: options.tlsOptions
  };
  this.pool = createPool(options, this);
}
util.inherits(ClientPool, EventEmitter);
module.exports = ClientPool;

STD_OPS.forEach(function (op) {
  ClientPool.prototype[op] = function clientProxy() {
    var args = Array.prototype.slice.call(arguments);
    var cb = args.pop();
    if (typeof (cb) !== 'function')
      throw new TypeError('callback (Function) required');
    var self = this;

    return this.pool.acquire(function onAcquire(err, client) {
      if (err)
        return cb(err);

      args.push(function proxyCallback(err, res) {
        self.pool.release(client);
        return cb(err, res);
      });

      try {
        return Client.prototype[op].apply(client, args);
      } catch (e) {
        self.pool.release(client);
        return cb(e);
      }
    });
  };
});

RETURN_VAL_OPS.forEach(function (op) {
  ClientPool.prototype[op] = function clientProxy() {
    var args = Array.prototype.slice.call(arguments);
    var cb = args.pop();
    if (typeof (cb) !== 'function')
      throw new TypeError('callback (Function) required');
    var self = this;

    return this.pool.acquire(function onAcquire(poolErr, client) {
      if (poolErr)
        return cb(poolErr);

      args.push(function proxyCallback(err, val, res) {
        self.pool.release(client);
        return cb(err, val, res);
      });

      try {
        return Client.prototype[op].apply(client, args);
      } catch (e) {
        self.pool.release(client);
        return cb(e);
      }
    });
  };
});

ClientPool.prototype.search = function search(base, opts, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  var self = this;

  return this.pool.acquire(function onAcquire(err, client) {
    if (err)
      return callback(err);

    // This is largely in existence for search requests
    client.timeout = self.timeout || client.timeout;

    return client.search(base, opts, controls, function (err, res) {
      function cleanup() {
        self.pool.release(client);
      }

      if (err) {
        cleanup();
        return callback(err, res);
      }
      res.on('error', cleanup);
      res.on('end', cleanup);

      return callback(null, res);
    });
  });
};

ClientPool.prototype.abandon = function abandon(msgid, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  this.log.error({
    messageID: msgid
  }, 'Abandon is not supported with connection pooling. Ignoring.');
  return callback(null);
};

ClientPool.prototype.bind = function bind(dn, creds, controls, callback) {
  if (typeof (controls) === 'function') {
    callback = controls;
    controls = [];
  }

  var self = this;

  self.options.bindDN = null;
  self.options.bindCredentials = null;
  self.options.bindControls = null;

  return this.pool.shutdown(function () {
    self.pool = createPool(self.options, self);

    return self.pool.acquire(function onAcquire(err, client) {
      if (err)
        return callback(err);

      return client.bind(dn, creds, controls, function (err, res) {
        self.pool.release(client);

        if (err)
          return callback(err, res);

        self.options.bindDN = dn;
        self.options.bindCredentials = creds;
        self.options.bindControls = controls;

        return callback(null, res);
      });
    });
  });
};

ClientPool.prototype.unbind = function unbind(callback) {
  return this.pool.shutdown(callback);
};
<|endoftext|>


<commit_before>const P = require('bluebird');
const _ = require('lodash');
const ColumnTypeGetter = require('./sequelize-column-type-getter');
const TableConstraintsGetter = require('./sequelize-table-constraints-getter');
const { DatabaseAnalyzerError } = require('../../utils/errors');
const { terminate } = require('../../utils/terminator');

let queryInterface;
let tableConstraintsGetter;
let columnTypeGetter;

function isUnderscored(fields) {
  return fields.every((field) => field.nameColumn === _.snakeCase(field.nameColumn))
    && fields.some((field) => field.nameColumn.includes('_'));
}

function analyzeFields(table, config) {
  return queryInterface.describeTable(table, { schema: config.dbSchema });
}

async function analyzePrimaryKeys(schema) {
  return Object.keys(schema).filter((column) => schema[column].primaryKey);
}

async function showAllTables(databaseConnection, schema) {
  const dbDialect = databaseConnection.getDialect();

  if (['mysql', 'mariadb'].includes(dbDialect)) {
    return queryInterface.showAllTables();
  }

  let realSchema = schema;
  if (!realSchema) {
    if (dbDialect === 'mssql') {
      [{ default_schema: realSchema }] = await queryInterface.sequelize.query('SELECT SCHEMA_NAME() as default_schema', { type: queryInterface.sequelize.QueryTypes.SELECT });
    } else {
      realSchema = 'public';
    }
  }

  return queryInterface.sequelize.query(
    'SELECT table_name as table_name FROM information_schema.tables WHERE table_schema = ? AND table_type LIKE \'%TABLE\' AND table_name != \'spatial_ref_sys\'',
    { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [realSchema] },
  )
    .then((results) => results.map((table) => table.table_name));
}

function hasTimestamps(fields) {
  let hasCreatedAt = false;
  let hasUpdatedAt = false;

  fields.forEach((field) => {
    if (field.name === 'createdAt') {
      hasCreatedAt = true;
    }

    if (field.name === 'updatedAt') {
      hasUpdatedAt = true;
    }
  });

  return hasCreatedAt && hasUpdatedAt;
}

function formatAliasName(columnName) {
  const alias = _.camelCase(columnName);
  if (alias.endsWith('Id') && alias.length > 2) {
    return alias.substring(0, alias.length - 2);
  }
  if (alias.endsWith('Uuid') && alias.length > 4) {
    return alias.substring(0, alias.length - 4);
  }
  return alias;
}

// NOTICE: Look for the id column in both fields and primary keys.
function hasIdColumn(fields, primaryKeys) {
  return fields.some((field) => field.name === 'id' || field.nameColumn === 'id')
    || _.includes(primaryKeys, 'id');
}

function analyzeTable(table, config) {
  return P
    .resolve(analyzeFields(table, config))
    .then((schema) => P.all([
      schema,
      tableConstraintsGetter.perform(table),
      analyzePrimaryKeys(schema),
    ]))
    .spread(async (schema, foreignKeys, primaryKeys) => {
      const fields = [];
      const references = [];

      await P.each(Object.keys(schema), async (nameColumn) => {
        const columnInfo = schema[nameColumn];
        const type = await columnTypeGetter.perform(columnInfo, nameColumn, table);
        const foreignKey = _.find(foreignKeys, { column_name: nameColumn });

        if (foreignKey
          && foreignKey.foreign_table_name
          && foreignKey.column_name
          && !columnInfo.primaryKey) {
          const reference = {
            ref: foreignKey.foreign_table_name,
            foreignKey: foreignKey.column_name,
            foreignKeyName: _.camelCase(foreignKey.column_name),
            as: formatAliasName(foreignKey.column_name),
          };

          // NOTICE: If the foreign key name and alias are the same, Sequelize will crash, we need
          //         to handle this specific scenario generating a different foreign key name.
          if (reference.foreignKeyName === reference.as) {
            reference.foreignKeyName = `${reference.foreignKeyName}Key`;
          }

          if (foreignKey.foreign_column_name !== 'id') {
            reference.targetKey = foreignKey.foreign_column_name;
          }

          references.push(reference);
        } else if (type) {
          // NOTICE: If the column is of integer type, named "id" and primary, Sequelize will
          //         handle it automatically without necessary declaration.
          if (!(nameColumn === 'id' && type === 'INTEGER' && columnInfo.primaryKey)) {
            // NOTICE: Handle bit(1) to boolean conversion
            let { defaultValue } = columnInfo;

            if (["b'1'", '((1))'].includes(defaultValue)) {
              defaultValue = true;
            }
            if (["b'0'", '((0))'].includes(defaultValue)) {
              defaultValue = false;
            }

            const field = {
              name: _.camelCase(nameColumn),
              nameColumn,
              type,
              primaryKey: columnInfo.primaryKey,
              defaultValue,
            };

            fields.push(field);
          }
        }
      });

      const options = {
        underscored: isUnderscored(fields),
        timestamps: hasTimestamps(fields),
        hasIdColumn: hasIdColumn(fields, primaryKeys),
        hasPrimaryKeys: !_.isEmpty(primaryKeys),
      };

      return {
        fields,
        references,
        primaryKeys,
        options,
      };
    });
}

async function analyzeSequelizeTables(databaseConnection, config, allowWarning) {
  const schema = {};

  queryInterface = databaseConnection.getQueryInterface();
  tableConstraintsGetter = new TableConstraintsGetter(databaseConnection, config.dbSchema);
  columnTypeGetter = new ColumnTypeGetter(databaseConnection, config.dbSchema || 'public', allowWarning);

  if (config.dbSchema) {
    const schemaExists = await queryInterface.sequelize
      .query(
        'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?;',
        { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [config.dbSchema] },
      )
      .then((result) => !!result.length);

    if (!schemaExists) {
      const message = 'This schema does not exists.';
      return terminate(1, {
        errorCode: 'database_authentication_error',
        errorMessage: message,
        logs: [message],
      });
    }
  }

  // Build the db schema.
  await P.mapSeries(showAllTables(databaseConnection, config.dbSchema), async (table) => {
    schema[table] = await analyzeTable(table, config);
  });

  if (_.isEmpty(schema)) {
    throw new DatabaseAnalyzerError.EmptyDatabase('no tables found', {
      orm: 'sequelize',
      dialect: databaseConnection.getDialect(),
    });
  }

  return schema;
}

module.exports = analyzeSequelizeTables;
<commit_msg>[-] Technical - Code improvement<commit_after>const P = require('bluebird');
const _ = require('lodash');
const ColumnTypeGetter = require('./sequelize-column-type-getter');
const TableConstraintsGetter = require('./sequelize-table-constraints-getter');
const { DatabaseAnalyzerError } = require('../../utils/errors');
const { terminate } = require('../../utils/terminator');

let queryInterface;
let tableConstraintsGetter;
let columnTypeGetter;

function isUnderscored(fields) {
  return fields.every((field) => field.nameColumn === _.snakeCase(field.nameColumn))
    && fields.some((field) => field.nameColumn.includes('_'));
}

function analyzeFields(table, config) {
  return queryInterface.describeTable(table, { schema: config.dbSchema });
}

async function analyzePrimaryKeys(schema) {
  return Object.keys(schema).filter((column) => schema[column].primaryKey);
}

async function showAllTables(databaseConnection, schema) {
  const dbDialect = databaseConnection.getDialect();

  if (['mysql', 'mariadb'].includes(dbDialect)) {
    return queryInterface.showAllTables();
  }

  let realSchema = schema;
  if (!realSchema) {
    if (dbDialect === 'mssql') {
      [{ default_schema: realSchema }] = await queryInterface.sequelize.query('SELECT SCHEMA_NAME() as default_schema', { type: queryInterface.sequelize.QueryTypes.SELECT });
    } else {
      realSchema = 'public';
    }
  }

  return queryInterface.sequelize.query(
    'SELECT table_name as table_name FROM information_schema.tables WHERE table_schema = ? AND table_type LIKE \'%TABLE\' AND table_name != \'spatial_ref_sys\'',
    { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [realSchema] },
  )
    .then((results) => results.map((table) => table.table_name));
}

function hasTimestamps(fields) {
  let hasCreatedAt = false;
  let hasUpdatedAt = false;

  fields.forEach((field) => {
    if (field.name === 'createdAt') {
      hasCreatedAt = true;
    }

    if (field.name === 'updatedAt') {
      hasUpdatedAt = true;
    }
  });

  return hasCreatedAt && hasUpdatedAt;
}

function formatAliasName(columnName) {
  const alias = _.camelCase(columnName);
  if (alias.endsWith('Id') && alias.length > 2) {
    return alias.substring(0, alias.length - 2);
  }
  if (alias.endsWith('Uuid') && alias.length > 4) {
    return alias.substring(0, alias.length - 4);
  }
  return alias;
}

// NOTICE: Look for the id column in both fields and primary keys.
function hasIdColumn(fields, primaryKeys) {
  return fields.some((field) => field.name === 'id' || field.nameColumn === 'id')
    || _.includes(primaryKeys, 'id');
}

function analyzeTable(table, config) {
  return analyzeFields(table, config)
    .then((schema) => P.all([
      schema,
      tableConstraintsGetter.perform(table),
      analyzePrimaryKeys(schema),
    ]))
    .spread(async (schema, foreignKeys, primaryKeys) => {
      const fields = [];
      const references = [];

      await P.each(Object.keys(schema), async (nameColumn) => {
        const columnInfo = schema[nameColumn];
        const type = await columnTypeGetter.perform(columnInfo, nameColumn, table);
        const foreignKey = _.find(foreignKeys, { column_name: nameColumn });

        if (foreignKey
          && foreignKey.foreign_table_name
          && foreignKey.column_name
          && !columnInfo.primaryKey) {
          const reference = {
            ref: foreignKey.foreign_table_name,
            foreignKey: foreignKey.column_name,
            foreignKeyName: _.camelCase(foreignKey.column_name),
            as: formatAliasName(foreignKey.column_name),
          };

          // NOTICE: If the foreign key name and alias are the same, Sequelize will crash, we need
          //         to handle this specific scenario generating a different foreign key name.
          if (reference.foreignKeyName === reference.as) {
            reference.foreignKeyName = `${reference.foreignKeyName}Key`;
          }

          if (foreignKey.foreign_column_name !== 'id') {
            reference.targetKey = foreignKey.foreign_column_name;
          }

          references.push(reference);
        } else if (type) {
          // NOTICE: If the column is of integer type, named "id" and primary, Sequelize will
          //         handle it automatically without necessary declaration.
          if (!(nameColumn === 'id' && type === 'INTEGER' && columnInfo.primaryKey)) {
            // NOTICE: Handle bit(1) to boolean conversion
            let { defaultValue } = columnInfo;

            if (["b'1'", '((1))'].includes(defaultValue)) {
              defaultValue = true;
            }
            if (["b'0'", '((0))'].includes(defaultValue)) {
              defaultValue = false;
            }

            const field = {
              name: _.camelCase(nameColumn),
              nameColumn,
              type,
              primaryKey: columnInfo.primaryKey,
              defaultValue,
            };

            fields.push(field);
          }
        }
      });

      const options = {
        underscored: isUnderscored(fields),
        timestamps: hasTimestamps(fields),
        hasIdColumn: hasIdColumn(fields, primaryKeys),
        hasPrimaryKeys: !_.isEmpty(primaryKeys),
      };

      return {
        fields,
        references,
        primaryKeys,
        options,
      };
    });
}

async function analyzeSequelizeTables(databaseConnection, config, allowWarning) {
  const schema = {};

  queryInterface = databaseConnection.getQueryInterface();
  tableConstraintsGetter = new TableConstraintsGetter(databaseConnection, config.dbSchema);
  columnTypeGetter = new ColumnTypeGetter(databaseConnection, config.dbSchema || 'public', allowWarning);

  if (config.dbSchema) {
    const schemaExists = await queryInterface.sequelize
      .query(
        'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?;',
        { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [config.dbSchema] },
      )
      .then((result) => !!result.length);

    if (!schemaExists) {
      const message = 'This schema does not exists.';
      return terminate(1, {
        errorCode: 'database_authentication_error',
        errorMessage: message,
        logs: [message],
      });
    }
  }

  // Build the db schema.
  await P.mapSeries(showAllTables(databaseConnection, config.dbSchema), async (table) => {
    schema[table] = await analyzeTable(table, config);
  });

  if (_.isEmpty(schema)) {
    throw new DatabaseAnalyzerError.EmptyDatabase('no tables found', {
      orm: 'sequelize',
      dialect: databaseConnection.getDialect(),
    });
  }

  return schema;
}

module.exports = analyzeSequelizeTables;
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import uniqBy from 'lodash/uniqBy';

const DEFAULT_LANGUAGE = 'EN';

const getCountries = state => state.countries.data;
const getSelected = state => state.document || null;
const getContent = state => state.content || null;

export const getCountry = createSelector(
  [getCountries, (_, iso) => iso],
  (countries, iso) => countries.find(country => country.iso_code3 === iso)
);

export const getSelectedContent = createSelector(
  [getSelected, getContent],
  (selected, content) => {
    if (!content || !content.length) return null;
    if (!selected) return content[0];
    const splitSelected = selected.split('-');
    return content.find(
      item =>
        item.document_type === splitSelected[0] &&
        (item.language === splitSelected[1] || DEFAULT_LANGUAGE),
      10
    );
  }
);

const getLabel = item =>
  `${item.document_type.toUpperCase()} (${item.language.toUpperCase()})`;

export const getContentOptions = createSelector(getContent, content =>
  uniqBy(
    (content || []).map(item => ({
      value: `${item.document_type}-${item.language}`,
      label: getLabel(item)
    })),
    'value'
  )
);

export const getContentOptionSelected = createSelector(
  [getSelected, getContentOptions],
  (selected, options) => {
    if (!selected) return options[0];
    // Allow finding document only by document name and default to English
    return (
      options.find(option => option.value === selected) ||
      options.find(
        option => option.value === `${selected}-${DEFAULT_LANGUAGE}`
      ) ||
      options.find(option => option.value.startsWith(selected))
    );
  }
);

export default {
  getCountry,
  getContent,
  getSelectedContent,
  getContentOptions
};
<commit_msg>Fix broken document selection when more than one language available on the NDC full content page<commit_after>import { createSelector } from 'reselect';
import uniqBy from 'lodash/uniqBy';

const DEFAULT_LANGUAGE = 'EN';

const getCountries = state => state.countries.data;
const getSelected = state => state.document || null;
const getContent = state => state.content || null;

export const getCountry = createSelector(
  [getCountries, (_, iso) => iso],
  (countries, iso) => countries.find(country => country.iso_code3 === iso)
);

export const getSelectedContent = createSelector(
  [getSelected, getContent],
  (selected, content) => {
    if (!content || !content.length) return null;
    if (!selected) return content[0];
    const splitSelected = selected.split('-');
    const selectedContent = content.find(
      ({ document_type, language }) =>
        document_type === splitSelected[0] && language === splitSelected[1]
    );
    if (selectedContent) return selectedContent;
    return content.find(
      ({ document_type, language }) =>
        document_type === splitSelected[0] && language === DEFAULT_LANGUAGE
    );
  }
);

const getLabel = item =>
  `${item.document_type.toUpperCase()} (${item.language.toUpperCase()})`;

export const getContentOptions = createSelector(getContent, content =>
  uniqBy(
    (content || []).map(item => ({
      value: `${item.document_type}-${item.language}`,
      label: getLabel(item)
    })),
    'value'
  )
);

export const getContentOptionSelected = createSelector(
  [getSelected, getContentOptions],
  (selected, options) => {
    if (!selected) return options[0];
    // Allow finding document only by document name and default to English
    return (
      options.find(option => option.value === selected) ||
      options.find(
        option => option.value === `${selected}-${DEFAULT_LANGUAGE}`
      ) ||
      options.find(option => option.value.startsWith(selected))
    );
  }
);

export default {
  getCountry,
  getContent,
  getSelectedContent,
  getContentOptions
};
<|endoftext|>