<commit_before>const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');

class File {

  constructor (options) {
    this.filename = options.filename.replace(/\\/g, '/');
    this.type = options.type;
    this.path = options.path;
    this.basedir = options.basedir;
    this.resolver = options.resolver;
  }

  parseFileForImport(content, isHttpContract, callback) {
    const self = this;
    if (typeof isHttpContract === 'function') {
      callback = isHttpContract;
      isHttpContract = false;
    }
    if (self.filename.indexOf('.sol') < 0) {
      // Only supported in Solidity
      return callback(null, content);
    }
    const regex = /import ["']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["'];/g;
    let matches;
    const filesToDownload = [];
    const pathWithoutFile = path.dirname(self.path);
    let newContent = content;
    while ((matches = regex.exec(content))) {
      const httpFileObj = utils.getExternalContractUrl(matches[1]);
      const fileObj = {
        fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
        url: `${pathWithoutFile}/${matches[1]}`
      };
      if (httpFileObj) {
        // Replace http import by filePath import in content
        newContent = newContent.replace(matches[1], httpFileObj.filePath);

        fileObj.fileRelativePath = httpFileObj.filePath;
        fileObj.url = httpFileObj.url;
      } else if (!isHttpContract) {
        // Just a normal import
        continue;
      }
      filesToDownload.push(fileObj);
    }

    if (self.downloadedImports) {
      // We already parsed this file
      return callback(null, newContent);
    }
    self.downloadedImports = true;
    async.each(filesToDownload, ((fileObj, eachCb) => {
      self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
        eachCb();
      });
    }), (err) => {
      callback(err, newContent);
    });
  }

  downloadFile (filename, url, callback) {
    const self = this;
    async.waterfall([
      function makeTheDir(next) {
        fs.mkdirp(path.dirname(filename), (err) => {
          if (err) {
            return next(err);
          }
          next();
        });
      },
      function downloadTheFile(next) {
        let alreadyCalledBack = false;
        function doCallback(err) {
          if (alreadyCalledBack) {
            return;
          }
          alreadyCalledBack = true;
          next(err);
        }
        request(url)
          .on('response', function (response) {
            if (response.statusCode !== 200) {
              doCallback('Getting file returned code ' + response.statusCode);
            }
          })
          .on('error', doCallback)
          .pipe(fs.createWriteStream(filename))
          .on('finish', () => {
            doCallback();
          });
      },
      function readFile(next) {
        fs.readFile(filename, next);
      },
      function parseForImports(content, next) {
        self.parseFileForImport(content, true, (err) => {
          next(err, content);
        });
      }
    ], (err, content) => {
      if (err) {
        console.error(__('Error while downloading the file'), err);
        return callback('');
      }
      callback(content.toString());
    });
  }

  content (callback) {
    let content;
    if (this.type === File.types.embark_internal) {
      content = fs.readFileSync(fs.embarkPath(this.path)).toString();
    } else if (this.type === File.types.dapp_file) {
      content = fs.readFileSync(this.path).toString();
    } else if (this.type === File.types.custom) {
      return this.resolver((theContent) => {
        this.parseFileForImport(theContent, (err, newContent) => {
          callback(newContent);
        });
      });
    } else if (this.type === File.types.http) {
      return this.downloadFile(this.filename, this.path, (content) => {
        if (!content) {
          return callback(content);
        }
        this.path = this.filename;
        this.type = File.types.dapp_file;
        callback(content);
      });
    } else {
      throw new Error("unknown file: " + this.filename);
    }
    return this.parseFileForImport(content, (err, newContent) => {
      callback(newContent);
    });
  }

}

File.types = {
  embark_internal: 'embark_internal',
  dapp_file: 'dapp_file',
  custom: 'custom',
  http: 'http'
};

module.exports = File;
<commit_msg>set downloaded flag at the very end<commit_after>const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');

class File {

  constructor (options) {
    this.filename = options.filename.replace(/\\/g, '/');
    this.type = options.type;
    this.path = options.path;
    this.basedir = options.basedir;
    this.resolver = options.resolver;
    this.downloadedImports = false;
  }

  parseFileForImport(content, isHttpContract, callback) {
    const self = this;
    if (typeof isHttpContract === 'function') {
      callback = isHttpContract;
      isHttpContract = false;
    }
    if (self.filename.indexOf('.sol') < 0) {
      // Only supported in Solidity
      return callback(null, content);
    }
    const regex = /import ["']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["'];/g;
    let matches;
    const filesToDownload = [];
    const pathWithoutFile = path.dirname(self.path);
    let newContent = content;
    while ((matches = regex.exec(content))) {
      const httpFileObj = utils.getExternalContractUrl(matches[1]);
      const fileObj = {
        fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
        url: `${pathWithoutFile}/${matches[1]}`
      };
      if (httpFileObj) {
        // Replace http import by filePath import in content
        newContent = newContent.replace(matches[1], httpFileObj.filePath);

        fileObj.fileRelativePath = httpFileObj.filePath;
        fileObj.url = httpFileObj.url;
      } else if (!isHttpContract) {
        // Just a normal import
        continue;
      }
      filesToDownload.push(fileObj);
    }

    if (self.downloadedImports) {
      // We already parsed this file
      return callback(null, newContent);
    }
    async.each(filesToDownload, ((fileObj, eachCb) => {
      self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
        eachCb();
      });
    }), (err) => {
      self.downloadedImports = true;
      callback(err, newContent);
    });
  }

  downloadFile (filename, url, callback) {
    const self = this;
    async.waterfall([
      function makeTheDir(next) {
        fs.mkdirp(path.dirname(filename), (err) => {
          if (err) {
            return next(err);
          }
          next();
        });
      },
      function downloadTheFile(next) {
        let alreadyCalledBack = false;
        function doCallback(err) {
          if (alreadyCalledBack) {
            return;
          }
          alreadyCalledBack = true;
          next(err);
        }
        request(url)
          .on('response', function (response) {
            if (response.statusCode !== 200) {
              doCallback('Getting file returned code ' + response.statusCode);
            }
          })
          .on('error', doCallback)
          .pipe(fs.createWriteStream(filename))
          .on('finish', () => {
            doCallback();
          });
      },
      function readFile(next) {
        fs.readFile(filename, next);
      },
      function parseForImports(content, next) {
        self.parseFileForImport(content.toString(), true, (err) => {
          next(err, content);
        });
      }
    ], (err, content) => {
      if (err) {
        console.error(__('Error while downloading the file'), err);
        return callback('');
      }
      callback(content.toString());
    });
  }

  content (callback) {
    let content;
    if (this.type === File.types.embark_internal) {
      content = fs.readFileSync(fs.embarkPath(this.path)).toString();
    } else if (this.type === File.types.dapp_file) {
      content = fs.readFileSync(this.path).toString();
    } else if (this.type === File.types.custom) {
      return this.resolver((theContent) => {
        this.parseFileForImport(theContent, (err, newContent) => {
          callback(newContent);
        });
      });
    } else if (this.type === File.types.http) {
      return this.downloadFile(this.filename, this.path, (content) => {
        if (!content) {
          return callback(content);
        }
        this.path = this.filename;
        this.type = File.types.dapp_file;
        callback(content);
      });
    } else {
      throw new Error("unknown file: " + this.filename);
    }
    return this.parseFileForImport(content, (err, newContent) => {
      callback(newContent);
    });
  }

}

File.types = {
  embark_internal: 'embark_internal',
  dapp_file: 'dapp_file',
  custom: 'custom',
  http: 'http'
};

module.exports = File;
<|endoftext|>


<commit_before>const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');

class File {

  constructor (options) {
    this.filename = options.filename;
    this.type = options.type;
    this.path = options.path;
    this.basedir = options.basedir;
    this.resolver = options.resolver;
  }

  parseFileForImport(content, isHttpContract, callback) {
    const self = this;
    if (typeof isHttpContract === 'function') {
      callback = isHttpContract;
      isHttpContract = false;
    }
    if (self.filename.indexOf('.sol') < 0) {
      // Only supported in Solidity
      return callback(null, content);
    }
    const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g;
    let matches;
    const filesToDownload = [];
    const pathWithoutFile = path.dirname(self.path);
    while ((matches = regex.exec(content))) {
      const httpFileObj = utils.getExternalContractUrl(matches[1]);
      const fileObj = {
        fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
        url: `${pathWithoutFile}/${matches[1]}`
      };
      if (httpFileObj) {
        // Replace http import by filePath import in content
        content = content.replace(matches[1], httpFileObj.filePath);

        fileObj.fileRelativePath = httpFileObj.filePath;
        fileObj.url = httpFileObj.url;
      } else if (!isHttpContract) {
        // Just a normal import
        continue;
      }
      filesToDownload.push(fileObj);
    }

    if (self.downloadedImports) {
      // We already parsed this file
      return callback(null, content);
    }
    self.downloadedImports = true;
    async.each(filesToDownload, ((fileObj, eachCb) => {
      self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
        eachCb();
      });
    }), (err) => {
      callback(err, content);
    });
  }

  downloadFile (filename, url, callback) {
    const self = this;
    async.waterfall([
      function makeTheDir(next) {
        fs.mkdirp(path.dirname(filename), (err) => {
          if (err) {
            return next(err);
          }
          next();
        });
      },
      function downloadTheFile(next) {
        request(url)
          .on('response', function (response) {
            if (response.statusCode !== 200) {
              next('Getting file returned code ' + response.statusCode);
            }
          })
          .on('error', next)
          .pipe(fs.createWriteStream(filename))
          .on('finish', next);
      },
      function readFile(next) {
        fs.readFile(filename, next);
      },
      function parseForImports(content, next) {
        self.parseFileForImport(content, true, (err) => {
          next(err, content);
        });
      }
    ], (err, content) => {
      if (err) {
        console.error('Error while downloading the file', err);
        return callback('');
      }
      callback(content.toString());
    });
  }

  content (callback) {
    let content;
    if (this.type === File.types.embark_internal) {
      content = fs.readFileSync(fs.embarkPath(this.path)).toString();
    } else if (this.type === File.types.dapp_file) {
      content = fs.readFileSync(this.path).toString();
    } else if (this.type === File.types.custom) {
      return this.resolver((theContent) => {
        this.parseFileForImport(theContent, (err, newContent) => {
          callback(newContent);
        });
      });
    } else if (this.type === File.types.http) {
      return this.downloadFile(this.filename, this.path, (content) => {
        if (!content) {
          return callback(content);
        }
        this.path = this.filename;
        this.type = File.types.dapp_file;
        callback(content);
      });
    } else {
      throw new Error("unknown file: " + this.filename);
    }
    return this.parseFileForImport(content, (err, newContent) => {
      callback(newContent);
    });
  }

}

File.types = {
  embark_internal: 'embark_internal',
  dapp_file: 'dapp_file',
  custom: 'custom',
  http: 'http'
};

module.exports = File;
<commit_msg>make sure all file names are using forward slashes<commit_after>const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');

class File {

  constructor (options) {
    this.filename = options.filename.replace(/\\/g, '/');
    this.type = options.type;
    this.path = options.path;
    this.basedir = options.basedir;
    this.resolver = options.resolver;
  }

  parseFileForImport(content, isHttpContract, callback) {
    const self = this;
    if (typeof isHttpContract === 'function') {
      callback = isHttpContract;
      isHttpContract = false;
    }
    if (self.filename.indexOf('.sol') < 0) {
      // Only supported in Solidity
      return callback(null, content);
    }
    const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g;
    let matches;
    const filesToDownload = [];
    const pathWithoutFile = path.dirname(self.path);
    while ((matches = regex.exec(content))) {
      const httpFileObj = utils.getExternalContractUrl(matches[1]);
      const fileObj = {
        fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
        url: `${pathWithoutFile}/${matches[1]}`
      };
      if (httpFileObj) {
        // Replace http import by filePath import in content
        content = content.replace(matches[1], httpFileObj.filePath);

        fileObj.fileRelativePath = httpFileObj.filePath;
        fileObj.url = httpFileObj.url;
      } else if (!isHttpContract) {
        // Just a normal import
        continue;
      }
      filesToDownload.push(fileObj);
    }

    if (self.downloadedImports) {
      // We already parsed this file
      return callback(null, content);
    }
    self.downloadedImports = true;
    async.each(filesToDownload, ((fileObj, eachCb) => {
      self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
        eachCb();
      });
    }), (err) => {
      callback(err, content);
    });
  }

  downloadFile (filename, url, callback) {
    const self = this;
    async.waterfall([
      function makeTheDir(next) {
        fs.mkdirp(path.dirname(filename), (err) => {
          if (err) {
            return next(err);
          }
          next();
        });
      },
      function downloadTheFile(next) {
        request(url)
          .on('response', function (response) {
            if (response.statusCode !== 200) {
              next('Getting file returned code ' + response.statusCode);
            }
          })
          .on('error', next)
          .pipe(fs.createWriteStream(filename))
          .on('finish', next);
      },
      function readFile(next) {
        fs.readFile(filename, next);
      },
      function parseForImports(content, next) {
        self.parseFileForImport(content, true, (err) => {
          next(err, content);
        });
      }
    ], (err, content) => {
      if (err) {
        console.error('Error while downloading the file', err);
        return callback('');
      }
      callback(content.toString());
    });
  }

  content (callback) {
    let content;
    if (this.type === File.types.embark_internal) {
      content = fs.readFileSync(fs.embarkPath(this.path)).toString();
    } else if (this.type === File.types.dapp_file) {
      content = fs.readFileSync(this.path).toString();
    } else if (this.type === File.types.custom) {
      return this.resolver((theContent) => {
        this.parseFileForImport(theContent, (err, newContent) => {
          callback(newContent);
        });
      });
    } else if (this.type === File.types.http) {
      return this.downloadFile(this.filename, this.path, (content) => {
        if (!content) {
          return callback(content);
        }
        this.path = this.filename;
        this.type = File.types.dapp_file;
        callback(content);
      });
    } else {
      throw new Error("unknown file: " + this.filename);
    }
    return this.parseFileForImport(content, (err, newContent) => {
      callback(newContent);
    });
  }

}

File.types = {
  embark_internal: 'embark_internal',
  dapp_file: 'dapp_file',
  custom: 'custom',
  http: 'http'
};

module.exports = File;
<|endoftext|>


<commit_before>var co = require('co')
	, thunkify = require('thunkify')
	, path = require('path')
	, fs = require('fs')
	, readFile = fs.readFileSync
	, exists = fs.existsSync
	, inherit = require('util').inherits
	, FileCache = require('../utils/FileCache')
	, identify = require('identify-resource').identify
	, resolve = require('identify-resource').resolve
	, lodash = require('lodash')
	, merge = lodash.merge
	, clone = lodash.clone
	, flatten = lodash.flatten
	, compact = lodash.compact
	, truncate = require('../utils/truncate')
	, cnsl = require('../utils/cnsl')
	, warn = cnsl.warn
	, debug = cnsl.debug
	, colour = cnsl.colour
	, print = cnsl.print
	, strong = cnsl.strong
	, comment = require('../utils/comment')
	, helpers = {
			write: require('./helpers/write'),
			compile: thunkify(require('transfigure')),
			compress: require('./helpers/compress'),
			concat: require('./helpers/concat'),
			inline: require('./helpers/inline'),
			lint: require('./helpers/lint'),
			escape: require('./helpers/escape'),
			parse: require('./helpers/parse'),
			replace: require('./helpers/replace'),
			wrap: require('./helpers/wrap')
		}

	, BOILERPLATE = fs.readFileSync(path.resolve(__filename, '../../../node_modules/simple-browser-require/require.js'), 'utf8')
	, HEADER = 'generated by Buddy '
		// Native Node modules to ignore
	, NATIVE_MODULES = [
			'assert',
			'buffer',
			'child_process',
			'cluster',
			'crypto',
			'dgram',
			'dns',
			'domain',
			'events',
			'fs',
			'http',
			'net',
			'os',
			'path',
			'punycode',
			'querystring',
			'readline',
			'repl',
			'stream',
			'string_decoder',
			'sys',
			'tls',
			'tty',
			'url',
			'util',
			'vm',
			'zlib'
		];

/**
 * File instance factory
 * @param {String} filepath
 * @param {Object} options
 * @returns {File}
 */
var factory = module.exports = function (filepath, options) {
	var file, id;

	if (factory.cache.hasFile(filepath)) {
		file = factory.cache.getFile(filepath);
	} else if (id = identify(filepath, options)) {
		if (factory.cache.hasFile(options.type + ':' + id)) {
			file = factory.cache.getFile(options.type + ':' + id);
		} else {
			file = new File(id, filepath, options);
			factory.cache.addFile(file);
		}
	}

	return file;
};

/*
 * Default file cache
 */
module.exports.cache = new FileCache();

/**
 * Constructor
 * @param {String} id
 * @param {String} filepath
 * @param {Object} options
 */
function File (id, filepath, options) {
	this.options = options;
	this.type = options.type;
	this.id = id;
	this.filepath = filepath;
	this.relPath = truncate(path.relative(process.cwd(), filepath));
	this.extension = path.extname(this.filepath).slice(1);
	this.name = path.basename(this.filepath);
	this.content = '';
	this.originalContent = '';
	this.compiledContent = '';
	this.dependencies = [];
	this.dependencyReferences = [];
	this.workflow = null;
	this.isRoot = false;
	this.isDependency = false;
	this.isLocked = false;

	debug("created "
		+ this.type
		+ " File instance "
		+ strong(this.relPath), 3);
}

/**
 * Retrieve writeable state
 * @returns {Boolean}
 */
File.prototype.getIsWriteable = function () {
	return this.isRoot || !this.isDependency;
};

/**
 * Retrieve flattened dependency tree
 * @param {Boolean} asReferences
 * @returns {Array}
 */
File.prototype.getDependencies = function (asReferences) {
	var self = this
		, deps = []
		, key = asReferences ? 'dependencyReferences' : 'dependencies';

	function add (dependency, dependant) {
		if ((dependency.instance || dependency) !== self && !~deps.indexOf(dependency)) {
			deps.push(dependency);
			(dependency.instance || dependency)[key].forEach(function (dep) {
				// Protect against circular references
				if ((dep.instance || dep) != dependant) add(dep, dependency);
			});
		}
	};

	this[key].forEach(add);

	return deps;
};

/**
 * Run 'workflow' tasks in sequence
 * @param {Array} workflow
 * @returns {Promise(File)}
 */
File.prototype.run = function * (workflow) {
	var dependencies, cmd;

	if (workflow && this.workflow != workflow) {

		this.workflow = workflow;
		// Execute commands in sequence
		for (var i = 0, n = workflow.length; i < n; i++) {
			cmd = workflow[i];
			if ('GeneratorFunction' == this[cmd].constructor.name) {
				yield this[cmd]();
			} else {
				this[cmd]();
			}
			// Parse needs special handling of dependencies
			if (cmd == 'parse') dependencies = this.dependencies;
		}
	}

	return dependencies;
};

/**
 * Read and store file contents
 * @returns {Function}
 */
File.prototype.load = function () {
	if (!this.originalContent) {
		var content = readFile(this.filepath, 'utf8');
		debug('load: ' + strong(this.relPath), 4);
		this.content = this.originalContent = content;
	} else {
		this.content = this.originalContent;
	}
};

/**
 * Compile file contents
 */
File.prototype.compile = function * () {
	// Only compile if not already
	if (!this.compiledContent) {
		var options = {}
			, dataUrl;

		// Expose properties for compilers
		options.id = this.id;
		options.type = this.type;
		if (this.type == 'html') {
			// Gather all dependencies
			options.includes = this.getDependencies();
			// Check/load data json file of same name in same directory
			if (fs.existsSync((dataUrl = path.resolve(path.dirname(this.filepath), this.name.replace(this.extension, 'json'))))) {
				options.data = require(dataUrl);
			}
		} else if (this.type == 'css') {
			// Gather all directories
			options.paths = factory.cache.getDirs();
		}

		this.content = this.compiledContent = yield helpers['compile'](this.filepath, this.content, options);
		debug('compile: ' + strong(this.relPath), 4);
	} else {
		this.content = this.compiledContent;
	}
};

/**
 * Parse file contents for dependency references
 * @returns {Array}
 */
File.prototype.parse = function () {
	var dependencies = helpers['parse'](this.filepath, this.type, this.content)
		, filepath, instance;

	if (dependencies) {
		dependencies.forEach(function (dependency) {
			// Validate and add
			filepath = resolve(this.filepath, dependency.filepath, this.options);
			instance = factory(filepath, this.options);
			if (filepath && instance) {
				// Save context for future inlining
				this.dependencyReferences.push(dependency);
				// Store instance
				dependency.instance = instance;
				// Process if not locked (parent target files are locked)
				if (!instance.isLocked) {
					// Store if not already stored
					if (!~this.dependencies.indexOf(instance)) {
						instance.isDependency = true;
						this.dependencies.push(instance);
					}
				}

			// Unable to resolve filepath
			} else {
				// Ignore native Node modules
				if (!(this.type == 'js' && ~NATIVE_MODULES.indexOf(dependency.filepath))) {
					warn("dependency "
						+ (strong(dependency.filepath))
						+ " for "
						+ (strong(this.id))
						+ " not found (ids are case-sensitive)", 4);
				}
			}
		}, this);
	}

	debug('parse: ' + strong(this.relPath), 4);

	return this.dependencies;
};

/**
 * Inline dependency content
 */
File.prototype.inline = function () {
	var deps = (this.type == 'html')
		? this.getDependencies(true)
		: this.dependencyReferences;

	this.content = helpers['inline'](this.filepath, this.type, this.content, deps);
	debug('inline: ' + strong(this.relPath), 4);
};

/**
 * Replace relative dependency references with fully resolved
 */
File.prototype.replace = function () {
	this.content = helpers['replace'](this.content, this.dependencyReferences);
	debug('replace: ' + strong(this.relPath), 4);
};

/**
 * Lint file contents
 */
File.prototype.lint = function () {
	// Don't lint compiled files or 3rd party modules
	if (this.extension == this.type && !~this.filepath.indexOf('node_modules')) {
		var warnings = helpers['lint'](this.type, this.content)
		if (warnings) {
			warn('linting ' + strong(this.relPath), 3);
			warnings.forEach(function (item) {
				if (item) {
					print("["
						+ (colour(item.line, cnsl.CYAN))
						+ ":"
						+ (colour(item.col, cnsl.CYAN))
						+ "] "
						+ item.reason
						+ ":", 4);
					if (item.evidence) print("" + (strong(item.evidence)), 5);
				}
			});
			return warnings;
		} else {
			debug('lint: ' + strong(this.relPath), 4);
		}
	}
};

/**
 * Escape file contents for lazy js modules
 */
File.prototype.escape = function () {
	this.content = helpers['escape'](this.content);
	debug('escape: ' + strong(this.relPath), 4);
};

/**
 * Compress file contents
 */
File.prototype.compress = function () {
	var type = (this.type == 'html')
		// Use extension to force compilation of inlineable content
		? this.extension
		: this.type;

	// TODO: trap errors here
	this.content = helpers['compress'](type, this.content);
	debug('compressed: ' + strong(this.relPath), 4);
};

/**
 * Wrap JS file contents in a module definition
 */
File.prototype.wrap = function () {
	var lazy = this.options.runtimeOptions
		? this.options.runtimeOptions.lazy
		: false;

	this.content = helpers['wrap'](this.id, this.content, lazy);
	debug('wrap: ' + strong(this.relPath), 4);
};

/**
 * Concatenate file contents
 */
File.prototype.concat = function () {
	this.content = helpers['concat'](this.type, this.content, this.getDependencies().reverse());
	debug('concat: ' + strong(this.relPath), 4);
};

/**
 * Write file contents to disk
 * @param {String} filepath
 * @param {Object} options
 * @returns {String}
 */
File.prototype.write = function * (filepath, options) {
	if (filepath) {
		options = options || {};

		// Add header
		this.content = comment(HEADER + (this.options.runtimeOptions.version || ''), this.type)
			+ '\n\n'
			+ this.content;

		// Add require boilerplate
		if (options.boilerplate) {
			this.content = BOILERPLATE
				+ '\n'
				+ this.content;
		}

		// Add bootstrap call
		if (options.bootstrap) {
			this.content += '\nrequire(\'' + this.id + '\');'
		}

		// Write
		yield helpers['write'](filepath, this.content);
		print(colour('built' + (this.options.runtimeOptions.compress ? ' and compressed' : ''), cnsl.GREEN)
			+ " "
			+ strong(path.relative(process.cwd(), filepath)), 3);

		return filepath;
	}
};

/**
 * Reset content
 * @param {Boolean} hard
 */
File.prototype.reset = function (hard) {
	this.workflow = null;
	this.isRoot = false;
	this.isLocked = false;
	this.isDependency = false;
	this.dependencies = [];
	this.dependencyReferences = [];
	if (this.type != 'js') {
		this.content = this.originalContent;
		this.compiledContent = '';
	} else {
		this.content = this.compiledContent || this.originalContent;
	}
	if (hard) {
		this.content = this.originalContent = this.compiledContent = '';
	}
};

/**
 * Destroy instance
 */
File.prototype.destroy = function () {
	this.reset(true);
	this.options = null;
};<commit_msg>move header comment to top<commit_after>var co = require('co')
	, thunkify = require('thunkify')
	, path = require('path')
	, fs = require('fs')
	, readFile = fs.readFileSync
	, exists = fs.existsSync
	, inherit = require('util').inherits
	, FileCache = require('../utils/FileCache')
	, identify = require('identify-resource').identify
	, resolve = require('identify-resource').resolve
	, lodash = require('lodash')
	, merge = lodash.merge
	, clone = lodash.clone
	, flatten = lodash.flatten
	, compact = lodash.compact
	, truncate = require('../utils/truncate')
	, cnsl = require('../utils/cnsl')
	, warn = cnsl.warn
	, debug = cnsl.debug
	, colour = cnsl.colour
	, print = cnsl.print
	, strong = cnsl.strong
	, comment = require('../utils/comment')
	, helpers = {
			write: require('./helpers/write'),
			compile: thunkify(require('transfigure')),
			compress: require('./helpers/compress'),
			concat: require('./helpers/concat'),
			inline: require('./helpers/inline'),
			lint: require('./helpers/lint'),
			escape: require('./helpers/escape'),
			parse: require('./helpers/parse'),
			replace: require('./helpers/replace'),
			wrap: require('./helpers/wrap')
		}

	, BOILERPLATE = fs.readFileSync(path.resolve(__filename, '../../../node_modules/simple-browser-require/require.js'), 'utf8')
	, HEADER = 'generated by Buddy '
		// Native Node modules to ignore
	, NATIVE_MODULES = [
			'assert',
			'buffer',
			'child_process',
			'cluster',
			'crypto',
			'dgram',
			'dns',
			'domain',
			'events',
			'fs',
			'http',
			'net',
			'os',
			'path',
			'punycode',
			'querystring',
			'readline',
			'repl',
			'stream',
			'string_decoder',
			'sys',
			'tls',
			'tty',
			'url',
			'util',
			'vm',
			'zlib'
		];

/**
 * File instance factory
 * @param {String} filepath
 * @param {Object} options
 * @returns {File}
 */
var factory = module.exports = function (filepath, options) {
	var file, id;

	if (factory.cache.hasFile(filepath)) {
		file = factory.cache.getFile(filepath);
	} else if (id = identify(filepath, options)) {
		if (factory.cache.hasFile(options.type + ':' + id)) {
			file = factory.cache.getFile(options.type + ':' + id);
		} else {
			file = new File(id, filepath, options);
			factory.cache.addFile(file);
		}
	}

	return file;
};

/*
 * Default file cache
 */
module.exports.cache = new FileCache();

/**
 * Constructor
 * @param {String} id
 * @param {String} filepath
 * @param {Object} options
 */
function File (id, filepath, options) {
	this.options = options;
	this.type = options.type;
	this.id = id;
	this.filepath = filepath;
	this.relPath = truncate(path.relative(process.cwd(), filepath));
	this.extension = path.extname(this.filepath).slice(1);
	this.name = path.basename(this.filepath);
	this.content = '';
	this.originalContent = '';
	this.compiledContent = '';
	this.dependencies = [];
	this.dependencyReferences = [];
	this.workflow = null;
	this.isRoot = false;
	this.isDependency = false;
	this.isLocked = false;

	debug("created "
		+ this.type
		+ " File instance "
		+ strong(this.relPath), 3);
}

/**
 * Retrieve writeable state
 * @returns {Boolean}
 */
File.prototype.getIsWriteable = function () {
	return this.isRoot || !this.isDependency;
};

/**
 * Retrieve flattened dependency tree
 * @param {Boolean} asReferences
 * @returns {Array}
 */
File.prototype.getDependencies = function (asReferences) {
	var self = this
		, deps = []
		, key = asReferences ? 'dependencyReferences' : 'dependencies';

	function add (dependency, dependant) {
		if ((dependency.instance || dependency) !== self && !~deps.indexOf(dependency)) {
			deps.push(dependency);
			(dependency.instance || dependency)[key].forEach(function (dep) {
				// Protect against circular references
				if ((dep.instance || dep) != dependant) add(dep, dependency);
			});
		}
	};

	this[key].forEach(add);

	return deps;
};

/**
 * Run 'workflow' tasks in sequence
 * @param {Array} workflow
 * @returns {Promise(File)}
 */
File.prototype.run = function * (workflow) {
	var dependencies, cmd;

	if (workflow && this.workflow != workflow) {

		this.workflow = workflow;
		// Execute commands in sequence
		for (var i = 0, n = workflow.length; i < n; i++) {
			cmd = workflow[i];
			if ('GeneratorFunction' == this[cmd].constructor.name) {
				yield this[cmd]();
			} else {
				this[cmd]();
			}
			// Parse needs special handling of dependencies
			if (cmd == 'parse') dependencies = this.dependencies;
		}
	}

	return dependencies;
};

/**
 * Read and store file contents
 * @returns {Function}
 */
File.prototype.load = function () {
	if (!this.originalContent) {
		var content = readFile(this.filepath, 'utf8');
		debug('load: ' + strong(this.relPath), 4);
		this.content = this.originalContent = content;
	} else {
		this.content = this.originalContent;
	}
};

/**
 * Compile file contents
 */
File.prototype.compile = function * () {
	// Only compile if not already
	if (!this.compiledContent) {
		var options = {}
			, dataUrl;

		// Expose properties for compilers
		options.id = this.id;
		options.type = this.type;
		if (this.type == 'html') {
			// Gather all dependencies
			options.includes = this.getDependencies();
			// Check/load data json file of same name in same directory
			if (fs.existsSync((dataUrl = path.resolve(path.dirname(this.filepath), this.name.replace(this.extension, 'json'))))) {
				options.data = require(dataUrl);
			}
		} else if (this.type == 'css') {
			// Gather all directories
			options.paths = factory.cache.getDirs();
		}

		this.content = this.compiledContent = yield helpers['compile'](this.filepath, this.content, options);
		debug('compile: ' + strong(this.relPath), 4);
	} else {
		this.content = this.compiledContent;
	}
};

/**
 * Parse file contents for dependency references
 * @returns {Array}
 */
File.prototype.parse = function () {
	var dependencies = helpers['parse'](this.filepath, this.type, this.content)
		, filepath, instance;

	if (dependencies) {
		dependencies.forEach(function (dependency) {
			// Validate and add
			filepath = resolve(this.filepath, dependency.filepath, this.options);
			instance = factory(filepath, this.options);
			if (filepath && instance) {
				// Save context for future inlining
				this.dependencyReferences.push(dependency);
				// Store instance
				dependency.instance = instance;
				// Process if not locked (parent target files are locked)
				if (!instance.isLocked) {
					// Store if not already stored
					if (!~this.dependencies.indexOf(instance)) {
						instance.isDependency = true;
						this.dependencies.push(instance);
					}
				}

			// Unable to resolve filepath
			} else {
				// Ignore native Node modules
				if (!(this.type == 'js' && ~NATIVE_MODULES.indexOf(dependency.filepath))) {
					warn("dependency "
						+ (strong(dependency.filepath))
						+ " for "
						+ (strong(this.id))
						+ " not found (ids are case-sensitive)", 4);
				}
			}
		}, this);
	}

	debug('parse: ' + strong(this.relPath), 4);

	return this.dependencies;
};

/**
 * Inline dependency content
 */
File.prototype.inline = function () {
	var deps = (this.type == 'html')
		? this.getDependencies(true)
		: this.dependencyReferences;

	this.content = helpers['inline'](this.filepath, this.type, this.content, deps);
	debug('inline: ' + strong(this.relPath), 4);
};

/**
 * Replace relative dependency references with fully resolved
 */
File.prototype.replace = function () {
	this.content = helpers['replace'](this.content, this.dependencyReferences);
	debug('replace: ' + strong(this.relPath), 4);
};

/**
 * Lint file contents
 */
File.prototype.lint = function () {
	// Don't lint compiled files or 3rd party modules
	if (this.extension == this.type && !~this.filepath.indexOf('node_modules')) {
		var warnings = helpers['lint'](this.type, this.content)
		if (warnings) {
			warn('linting ' + strong(this.relPath), 3);
			warnings.forEach(function (item) {
				if (item) {
					print("["
						+ (colour(item.line, cnsl.CYAN))
						+ ":"
						+ (colour(item.col, cnsl.CYAN))
						+ "] "
						+ item.reason
						+ ":", 4);
					if (item.evidence) print("" + (strong(item.evidence)), 5);
				}
			});
			return warnings;
		} else {
			debug('lint: ' + strong(this.relPath), 4);
		}
	}
};

/**
 * Escape file contents for lazy js modules
 */
File.prototype.escape = function () {
	this.content = helpers['escape'](this.content);
	debug('escape: ' + strong(this.relPath), 4);
};

/**
 * Compress file contents
 */
File.prototype.compress = function () {
	var type = (this.type == 'html')
		// Use extension to force compilation of inlineable content
		? this.extension
		: this.type;

	// TODO: trap errors here
	this.content = helpers['compress'](type, this.content);
	debug('compressed: ' + strong(this.relPath), 4);
};

/**
 * Wrap JS file contents in a module definition
 */
File.prototype.wrap = function () {
	var lazy = this.options.runtimeOptions
		? this.options.runtimeOptions.lazy
		: false;

	this.content = helpers['wrap'](this.id, this.content, lazy);
	debug('wrap: ' + strong(this.relPath), 4);
};

/**
 * Concatenate file contents
 */
File.prototype.concat = function () {
	this.content = helpers['concat'](this.type, this.content, this.getDependencies().reverse());
	debug('concat: ' + strong(this.relPath), 4);
};

/**
 * Write file contents to disk
 * @param {String} filepath
 * @param {Object} options
 * @returns {String}
 */
File.prototype.write = function * (filepath, options) {
	if (filepath) {
		options = options || {};

		// Add require boilerplate
		if (options.boilerplate) {
			this.content = BOILERPLATE
				+ '\n'
				+ this.content;
		}

		// Add header
		this.content = comment(HEADER + (this.options.runtimeOptions.version || ''), this.type)
			+ '\n\n'
			+ this.content;

		// Add bootstrap call
		if (options.bootstrap) {
			this.content += '\nrequire(\'' + this.id + '\');'
		}

		// Write
		yield helpers['write'](filepath, this.content);
		print(colour('built' + (this.options.runtimeOptions.compress ? ' and compressed' : ''), cnsl.GREEN)
			+ " "
			+ strong(path.relative(process.cwd(), filepath)), 3);

		return filepath;
	}
};

/**
 * Reset content
 * @param {Boolean} hard
 */
File.prototype.reset = function (hard) {
	this.workflow = null;
	this.isRoot = false;
	this.isLocked = false;
	this.isDependency = false;
	this.dependencies = [];
	this.dependencyReferences = [];
	if (this.type != 'js') {
		this.content = this.originalContent;
		this.compiledContent = '';
	} else {
		this.content = this.compiledContent || this.originalContent;
	}
	if (hard) {
		this.content = this.originalContent = this.compiledContent = '';
	}
};

/**
 * Destroy instance
 */
File.prototype.destroy = function () {
	this.reset(true);
	this.options = null;
};<|endoftext|>


<commit_before>/**
 * Adleria bindings for nodejs
 *
 * (c) 2015 Optimal Bits Sweden AB.
 *
 */
/// <reference path="../typings/node/node.d.ts"/>
"use strict";

var request = require("request-promise");
var urlJoin = require("url-join");
var _ = require("lodash");

module.exports = function (auth, baseUrl, opts) {
  var url = urlJoin(baseUrl, "customers");

  return {
    list: function (query) {
      return request({
        url: url,
        method: "GET",
        json: true,
        auth,
        qs: query,
      });
    },
    get: function (id) {
      return request({
        url: urlJoin(url, id),
        method: "GET",
        json: true,
        auth,
      });
    },
    edit: function (id, args) {
      return request({
        url: urlJoin(url, id),
        method: "PUT",
        body: args,
        json: true,
        auth,
      });
    },
    create: function (body) {
      return request({
        url,
        method: "POST",
        body,
        json: true,
        auth,
      });
    },
    connect: {
      /**
       * Gets a Stripe Connect link in order to allow this
       * customer to get paid fees for showing ads in its
       * adspaces.
       */
      link: function (customerId, returnUrl) {
        if (customerId) {
          return request({
            url: `${urlJoin(
              url,
              customerId,
              "connect"
            )}?returnUrl=${returnUrl}`,
            method: "GET",
            auth,
          });
        } else {
          return request({
            url: urlJoin(url, "connect"),
            method: "GET",
            auth,
          });
        }
      },
    },
    adspaces: {
      create: function (customerId, args) {
        return request({
          url: urlJoin(url, customerId, "adspaces"),
          method: "POST",
          body: args,
          json: true,
          auth,
        });
      },
      list: function (customerId, filters) {
        return request({
          url: urlJoin(url, customerId, "adspaces"),
          method: "GET",
          json: true,
          auth,
          qs: filters,
        });
      },
      edit: function (customerId, adspaceId, args) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "PUT",
          json: true,
          auth,
          body: args,
        });
      },
      get: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "GET",
          json: true,
          auth,
        });
      },
      delete: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "DELETE",
          json: true,
          auth,
        });
      },
      submit: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "status/submit"),
          method: "PUT",
          auth,
        });
      },
      getCampaigns: function (customerId, adspaceId, status) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "campaigns"),
          method: "GET",
          json: true,
          auth,
          qs: {
            status,
          },
        });
      },
      acceptCampaign: function (customerId, adspaceId, campaignId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "accept"),
          method: "PUT",
          json: true,
          body: {
            campaignId,
          },
          auth,
        });
      },
      rejectCampaign: function (customerId, adspaceId, campaignId, reason) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "reject"),
          method: "PUT",
          json: true,
          body: {
            campaignId,
            reason,
          },
          auth,
        });
      },
      getImpressions: function (customerId, adspaceId, options) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "impressions"),
          method: "GET",
          json: true,
          auth,
          qs: options || {},
        });
      },
      players: {
        create: function (customerId, adspaceId, player) {
          var data = _.pick(player, "name");
          return request({
            url: urlJoin(url, customerId, "adspaces", adspaceId, "players"),
            method: "POST",
            body: data,
            json: true,
            auth,
          });
        },
        assign: function (customerId, adspaceId, playerId) {
          return request({
            url: urlJoin(
              url,
              customerId,
              "adspaces",
              adspaceId,
              "players",
              playerId
            ),
            method: "PUT",
            auth,
          });
        },
        unassign: function (customerId, adspaceId, playerId) {
          return request({
            url: urlJoin(
              url,
              customerId,
              "adspaces",
              adspaceId,
              "players",
              playerId
            ),
            method: "DELETE",
            auth,
          });
        },
        list: function (customerId, adspaceId, qs) {
          return request({
            url: urlJoin(url, customerId, "adspaces", adspaceId, "players"),
            method: "GET",
            json: true,
            auth,
            qs: qs || {},
          });
        },
      },
    },
    invoices: {
      get: function (customerId, adspaceId, options) {
        return request({
          url: urlJoin(url, customerId, "invoices", adspaceId),
          method: "GET",
          json: true,
          auth,
          qs: options,
        });
      },
      list: function (customerId, filters) {
        return request({
          url: urlJoin(url, customerId, "invoices"),
          method: "GET",
          json: true,
          auth,
          qs: filters,
        });
      },
    },
  };
};
<commit_msg>feat(adspaces): support for adding and removing tags<commit_after>/**
 * Adleria bindings for nodejs
 *
 * (c) 2015 Optimal Bits Sweden AB.
 *
 */
/// <reference path="../typings/node/node.d.ts"/>
"use strict";

var request = require("request-promise");
var urlJoin = require("url-join");
var _ = require("lodash");

module.exports = function (auth, baseUrl, opts) {
  var url = urlJoin(baseUrl, "customers");

  return {
    list: function (query) {
      return request({
        url: url,
        method: "GET",
        json: true,
        auth,
        qs: query,
      });
    },
    get: function (id) {
      return request({
        url: urlJoin(url, id),
        method: "GET",
        json: true,
        auth,
      });
    },
    edit: function (id, args) {
      return request({
        url: urlJoin(url, id),
        method: "PUT",
        body: args,
        json: true,
        auth,
      });
    },
    create: function (body) {
      return request({
        url,
        method: "POST",
        body,
        json: true,
        auth,
      });
    },
    connect: {
      /**
       * Gets a Stripe Connect link in order to allow this
       * customer to get paid fees for showing ads in its
       * adspaces.
       */
      link: function (customerId, returnUrl) {
        if (customerId) {
          return request({
            url: `${urlJoin(
              url,
              customerId,
              "connect"
            )}?returnUrl=${returnUrl}`,
            method: "GET",
            auth,
          });
        } else {
          return request({
            url: urlJoin(url, "connect"),
            method: "GET",
            auth,
          });
        }
      },
    },
    adspaces: {
      create: function (customerId, args) {
        return request({
          url: urlJoin(url, customerId, "adspaces"),
          method: "POST",
          body: args,
          json: true,
          auth,
        });
      },
      list: function (customerId, filters) {
        return request({
          url: urlJoin(url, customerId, "adspaces"),
          method: "GET",
          json: true,
          auth,
          qs: filters,
        });
      },
      edit: function (customerId, adspaceId, args) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "PUT",
          json: true,
          auth,
          body: args,
        });
      },
      get: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "GET",
          json: true,
          auth,
        });
      },
      delete: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId),
          method: "DELETE",
          json: true,
          auth,
        });
      },
      addTags: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "tags"),
          method: "PUT",
          auth,
        });
      },
      removeTags: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "tags"),
          method: "DELETE",
          auth,
        });
      },
      submit: function (customerId, adspaceId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "status/submit"),
          method: "PUT",
          auth,
        });
      },
      getCampaigns: function (customerId, adspaceId, status) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "campaigns"),
          method: "GET",
          json: true,
          auth,
          qs: {
            status,
          },
        });
      },
      acceptCampaign: function (customerId, adspaceId, campaignId) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "accept"),
          method: "PUT",
          json: true,
          body: {
            campaignId,
          },
          auth,
        });
      },
      rejectCampaign: function (customerId, adspaceId, campaignId, reason) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "reject"),
          method: "PUT",
          json: true,
          body: {
            campaignId,
            reason,
          },
          auth,
        });
      },
      getImpressions: function (customerId, adspaceId, options) {
        return request({
          url: urlJoin(url, customerId, "adspaces", adspaceId, "impressions"),
          method: "GET",
          json: true,
          auth,
          qs: options || {},
        });
      },
      players: {
        create: function (customerId, adspaceId, player) {
          var data = _.pick(player, "name");
          return request({
            url: urlJoin(url, customerId, "adspaces", adspaceId, "players"),
            method: "POST",
            body: data,
            json: true,
            auth,
          });
        },
        assign: function (customerId, adspaceId, playerId) {
          return request({
            url: urlJoin(
              url,
              customerId,
              "adspaces",
              adspaceId,
              "players",
              playerId
            ),
            method: "PUT",
            auth,
          });
        },
        unassign: function (customerId, adspaceId, playerId) {
          return request({
            url: urlJoin(
              url,
              customerId,
              "adspaces",
              adspaceId,
              "players",
              playerId
            ),
            method: "DELETE",
            auth,
          });
        },
        list: function (customerId, adspaceId, qs) {
          return request({
            url: urlJoin(url, customerId, "adspaces", adspaceId, "players"),
            method: "GET",
            json: true,
            auth,
            qs: qs || {},
          });
        },
      },
    },
    invoices: {
      get: function (customerId, adspaceId, options) {
        return request({
          url: urlJoin(url, customerId, "invoices", adspaceId),
          method: "GET",
          json: true,
          auth,
          qs: options,
        });
      },
      list: function (customerId, filters) {
        return request({
          url: urlJoin(url, customerId, "invoices"),
          method: "GET",
          json: true,
          auth,
          qs: filters,
        });
      },
    },
  };
};
<|endoftext|>


<commit_before>var sqlite3 = require('sqlite3').verbose(),
    templates = require('./sql_templates'),
    utils = require('../utils'),
    fs = require('fs');

module.exports = utils.inherit(Object, {
  defaults: null,
  constructor: function SQLite(options) {
    this.defaults = {html: '', css: '', javascript: ''};
    this.database = options.location;
  },
  connect: function (fn) {
    var self = this;
    this.connection = new sqlite3.Database(this.database, function () {
      fs.readFile(__dirname + '/../../build/full-db-v3.sqlite.sql', 'utf8', function (err, sql) {
        if (err) {
          return fn(err);
        }
        self.connection.serialize(function () {
          sql = sql.trim();
          if (sql) {
            self.connection.exec(sql, fn);
          } else {
            fn();
          }
        });
      });
    });
  },
  disconnect: function (fn) {
    this.connection.close();
    fn();
  },
  getBin: function (params, fn) {
    var values = [params.id, params.revision],
        _this = this;

    this.connection.get(templates.getBin, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertBinDates(result);
      }

      fn(null, result && _this.applyBinDefaults(result));
    });
  },
  setBin: function (params, fn) {
    var now = new Date(), values = [
      params.javascript || '',
      params.css || '',
      params.html || '',
      now,
      now,
      params.url,
      params.revision,
      params.streamingKey,
      params.settings
    ], sql = templates.setBin;

    this.connection.run(sql, values, function (err) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  setBinOwner: function (params, fn) {
    var sql = templates.setBinOwner,
        values = [params.name, params.url, params.revision, new Date(), params.summary, params.html, params.css, params.javascript];

    // TODO: Re-factor common callbacks into helpers.
    this.connection.run(sql, values, function (err) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  setBinPanel: function (panel, params, fn) {
    var values = [
      params[panel],
      params.settings,
      new Date(),
      params.url,
      params.revision,
      params.streamingKey
    ],
    allowed = {html: 1, css: 1, javascript: 1},
    sql = templates.setBinPanel.replace(':panel', panel);

    if (allowed[panel]) {
      this.connection.run(sql, values, function (err) {
        if (err || !this.changes) {
          return fn(err || 'no-entry');
        }
        fn(null, this.lastID);
      });
    } else {
      fn('invalid-panel');
    }
  },
  getLatestBin: function (params, fn) {
    var values = [params.id],
        sql = templates.getLatestBin,
        _this = this;

    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertBinDates(result);
      }

      fn(null, result && _this.applyBinDefaults(result));
    });
  },
  getLatestBinForUser: function (id, n, fn) {
    var sql = templates.getLatestBinForUser,
        query = this.connection.get.bind(this.connection),
        _this = this;

    query(sql, [id, n], function (err, result) {
      var sql = templates.getBinByUrlAndRevision;

      if (err) {
        return fn(err);
      }

      if (typeof result === 'undefined') {
        return fn(null, null);
      }

      _this.getBin({ id: result.url, revision: result.revision }, fn);
    });
  },
  getBinsByUser: function (id, fn) {
    var sql = templates.getBinsByUser,
        _this = this;

    this.connection.all(sql, [id], function (err, results) {
      if (err) {
        return fn(err);
      }

      var sql = templates.getBinByUrlAndRevision,
          collected = [];

      // i.e. if they've never saved anything before
      results.forEach(function (result) {
        collected.push(_this.applyBinDefaults(result));
      });
      fn(null, collected);
    });
  },
  // Get all bins from the owners field
  getAllOwners: function (fn) {
    // Get all the 'owned' bins
    this.connection.run(templates.getAllOwners, [], fn);
  },
  getOwnersBlock: function (start, size, fn) {
    // Get all the 'owned' bins
    this.connection.run(templates.getOwnersBlock, [start, size], fn);
  },
  generateBinId: function (fn, attempts) {
    var id = utils.shortcode(), mysql = this;

    attempts = attempts || 1;
    if (attempts <= 10) {
      this.connection.get(templates.binExists, [id], function (err, result) {
        if (err) {
          fn(err);
        } else if (result) {
          attempts += 1;
          mysql.generateBinId(fn, attempts);
        } else {
          fn(null, id);
        }
      });
    } else {
      fn(new Error("too-many-tries"));
    }
  },
  // getOne('<sql template>', [constraint1, constraint2, ...], fn)
  getOne: function (queryKey, params, fn) {
    this.connection.get(templates[queryKey], params, function (err, result) {
      if (err) {
        fn(err, null);
      } else {
        fn(null, result);
      }
    });
  },
  getUser: function (id, fn) {
    var _this = this;

    this.connection.get(templates.getUser, [id], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (!result) {
        _this.connection.get(templates.getUserByEmail, [id], function (err, result) {
          if (err) {
            return fn(err);
          }

          if (result) {
            result = _this.convertUserDates(result);
          }

          fn(null, result);
        });
      } else {
        if (result) {
          result = _this.convertUserDates(result);
        }
        fn(null, result);
      }
    });
  },
  getUserByApiKey: function (apiKey, fn) {
    var _this = this;

    this.connection.get(templates.getUserByApiKey, [apiKey], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }
      fn(null, result);
    });
  },
  getUserByEmail: function (email, fn) {
    var _this = this;

    this.connection.get(templates.getUserByEmail, [email], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }

      fn(null, result);
    });
  },
  setUser: function (params, fn) {
    var now = new Date(), values = [
      params.name,
      params.key,
      params.email,
      now,
      now,
      now,
      params.github_token,
      params.github_id
    ], sql = templates.setUser;

    this.connection.run(sql, values, function (err) {
      if (err) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  touchOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.name, params.url, params.revision];

    this.connection.run(templates.touchOwners, values, function (err) {
      if (err) {
        return fn(err);
      }

      if (typeof fn === 'function') {
        fn(null);
      }
    });
  },
  updateOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.summary, params.panel_open, params.name, params.url, params.revision];

    var panel = params.panel,
        allowed = {html: 1, css: 1, javascript: 1},
        sql = templates.updateOwners.replace(':panel', panel);

    if (allowed[panel]) {
      this.connection.run(sql, values, function (err) {
        if (err) {
          return fn(err);
        }

        if (typeof fn === 'function') {
          fn(null);
        }
      });
    } else {
      fn('invalid-panel');
    }
  },
  populateOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.summary, params.html, params.css, params.javascript, params.name, params.url, params.revision];

    this.connection.run(templates.populateOwners, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (typeof fn === 'function') {
        fn(null);
      }
    });
  },
  touchLogin: function (id, fn) {
    var now = new Date();
    this.connection.run(templates.touchLogin, [now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserEmail: function (id, email, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserEmail, [email, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserGithubData: function (id, ghId, token, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserGithubData, [ghId, token, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserKey: function (id, key, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserKey, [key, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  // Different to updateUserKey() in that it also sets the created timestamp
  // which is required to differentiate between a JSBin 2 user and a new
  // one.
  upgradeUserKey: function (id, key, fn) {
    var now = new Date();
    this.connection.run(templates.upgradeUserKey, [key, now, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  getUserByForgotToken: function (token, fn) {
    var sql = templates.getUserForForgotToken,
        _this = this;

    this.connection.get(sql, [token, new Date()], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }

      fn(null, result);
    });
  },
  setForgotToken: function (user, token, fn) {
    var sql = templates.setForgotToken,
        expires = this.expireDate(),
        params = [user, token, expires, new Date()];

    this.connection.run(sql, params, function (err) {
      if (err) {
        return fn(err);
      }
      fn();
    });
  },
  expireForgotToken: function (token, fn) {
    var sql = templates.deleteExpiredForgotToken;

    // Allow all old tokens to be expired with same call.
    if (typeof token === 'function') {
      fn = token;
      token = null;
    }

    this.connection.run(sql, [new Date(), token, null], function (err, results) {
      fn(err || null);
    });
  },
  expireForgotTokenByUser: function (user, fn) {
    var sql = templates.deleteExpiredForgotToken;

    this.connection.run(sql, [new Date(), null, user], function (err) {
      fn(err || null);
    });
  },
  expireDate: function () {
    var expires = new Date();
    expires.setUTCDate(expires.getUTCDate() + 1);
    return expires;
  },
  applyBinDefaults: function (bin) {
    for (var prop in this.defaults) {
      if (bin[prop] == null) { // Using == to catch null and undefined.
        bin[prop] = this.defaults[prop];
      }
    }

    this.convertDates(bin, 'last_updated');

    bin.active = bin.active === 'y';

    if (!bin.last_updated || isNaN(bin.last_updated.getTime())) bin.last_updated = new Date('2012-07-23 00:00:00');

    try {
      bin.settings = JSON.parse(bin.settings || '{}');
    } catch (e) {
      // this is likely because the settings were screwed in a beta build
      bin.settings = {};
    }
    // console.log(bin);

    return bin;
  },
  convertUserDates: function (user) {
    return this.convertDates(user, 'created', 'updated', 'last_login');
  },
  convertBinDates: function (bin) {
    return this.convertDates(bin, 'created', 'last_viewed');
  },
  convertDates: function (obj/* keys */) {
    var keys = [].slice.call(arguments, 1);
    keys.forEach(function (key) {
      if (obj && obj[key]) {
        var date = new Date();
        date.setTime(obj[key]);
        obj[key] = date;
      }
    });
    return obj;
  },
  reportBin: function (params, fn) {
    var now = new Date(), values = [
      now,
      params.url,
      params.revision
    ], sql = templates.reportBin;

    this.connection.run(sql, values, function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  archiveBin: function (bin, fn) {
    var values = [bin.archive, bin.name, bin.url, bin.revision],
        sql = templates.archiveBin;

    this.connection.run(sql, values, function (err, result) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, result);
    });
  },
  isOwnerOf: function (params, fn) {
    var values = [
      params.name || '',
      params.url
    ], sql = templates.isOwnerOf;

    // note: .get gets one row
    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }
      if (typeof result === 'undefined') {
        return fn(null, { found: false });
      } else {
        return fn(null, { found: true, isowner: result.owner === 1 });
      }
    });
  },
  getUserBinCount: function (id, fn) {
    var values = [id],
        sql = templates.getUserBinCount;

    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }
      if (typeof result === 'undefined') {
        return fn(null, { found: false, total: 0 });
      } else {
        return fn(null, { found: true, total: result.total });
      }
    });
  },
  getBinMetadata: function(bin, fn) {
    var sql = templates.getBinMetadata;
    this.connection.get(sql, [bin.url, bin.revision], function(err, result) {
      if (err) {
        return fn(err);
      }
      fn(null, result[0] ? result[0] : {
        visibility: 'public',
        name: 'anon'
      });
    });
  },
  setBinVisibility: function(bin, name, value, fn) {
    var sql = templates.setBinVisibility, params = [
      value, bin.metadata.name, bin.url
    ];
    if (bin.metadata.name !== name) {
      return fn(301)
    }
    this.connection.run(sql, params, function(err, result) {
      if (err) {
        return fn(500);
      }
      fn(err, result);
    });
  },
  setProAccount: function(id, pro, fn){
    var now = new Date();
    this.connection.run(templates.setProAccount, [pro, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  }
});
<commit_msg>Updating sqlite adaptor setBinVisibility<commit_after>var sqlite3 = require('sqlite3').verbose(),
    templates = require('./sql_templates'),
    utils = require('../utils'),
    fs = require('fs');

module.exports = utils.inherit(Object, {
  defaults: null,
  constructor: function SQLite(options) {
    this.defaults = {html: '', css: '', javascript: ''};
    this.database = options.location;
  },
  connect: function (fn) {
    var self = this;
    this.connection = new sqlite3.Database(this.database, function () {
      fs.readFile(__dirname + '/../../build/full-db-v3.sqlite.sql', 'utf8', function (err, sql) {
        if (err) {
          return fn(err);
        }
        self.connection.serialize(function () {
          sql = sql.trim();
          if (sql) {
            self.connection.exec(sql, fn);
          } else {
            fn();
          }
        });
      });
    });
  },
  disconnect: function (fn) {
    this.connection.close();
    fn();
  },
  getBin: function (params, fn) {
    var values = [params.id, params.revision],
        _this = this;

    this.connection.get(templates.getBin, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertBinDates(result);
      }

      fn(null, result && _this.applyBinDefaults(result));
    });
  },
  setBin: function (params, fn) {
    var now = new Date(), values = [
      params.javascript || '',
      params.css || '',
      params.html || '',
      now,
      now,
      params.url,
      params.revision,
      params.streamingKey,
      params.settings
    ], sql = templates.setBin;

    this.connection.run(sql, values, function (err) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  setBinOwner: function (params, fn) {
    var sql = templates.setBinOwner,
        values = [params.name, params.url, params.revision, new Date(), params.summary, params.html, params.css, params.javascript];

    // TODO: Re-factor common callbacks into helpers.
    this.connection.run(sql, values, function (err) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  setBinPanel: function (panel, params, fn) {
    var values = [
      params[panel],
      params.settings,
      new Date(),
      params.url,
      params.revision,
      params.streamingKey
    ],
    allowed = {html: 1, css: 1, javascript: 1},
    sql = templates.setBinPanel.replace(':panel', panel);

    if (allowed[panel]) {
      this.connection.run(sql, values, function (err) {
        if (err || !this.changes) {
          return fn(err || 'no-entry');
        }
        fn(null, this.lastID);
      });
    } else {
      fn('invalid-panel');
    }
  },
  getLatestBin: function (params, fn) {
    var values = [params.id],
        sql = templates.getLatestBin,
        _this = this;

    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertBinDates(result);
      }

      fn(null, result && _this.applyBinDefaults(result));
    });
  },
  getLatestBinForUser: function (id, n, fn) {
    var sql = templates.getLatestBinForUser,
        query = this.connection.get.bind(this.connection),
        _this = this;

    query(sql, [id, n], function (err, result) {
      var sql = templates.getBinByUrlAndRevision;

      if (err) {
        return fn(err);
      }

      if (typeof result === 'undefined') {
        return fn(null, null);
      }

      _this.getBin({ id: result.url, revision: result.revision }, fn);
    });
  },
  getBinsByUser: function (id, fn) {
    var sql = templates.getBinsByUser,
        _this = this;

    this.connection.all(sql, [id], function (err, results) {
      if (err) {
        return fn(err);
      }

      var sql = templates.getBinByUrlAndRevision,
          collected = [];

      // i.e. if they've never saved anything before
      results.forEach(function (result) {
        collected.push(_this.applyBinDefaults(result));
      });
      fn(null, collected);
    });
  },
  // Get all bins from the owners field
  getAllOwners: function (fn) {
    // Get all the 'owned' bins
    this.connection.run(templates.getAllOwners, [], fn);
  },
  getOwnersBlock: function (start, size, fn) {
    // Get all the 'owned' bins
    this.connection.run(templates.getOwnersBlock, [start, size], fn);
  },
  generateBinId: function (fn, attempts) {
    var id = utils.shortcode(), mysql = this;

    attempts = attempts || 1;
    if (attempts <= 10) {
      this.connection.get(templates.binExists, [id], function (err, result) {
        if (err) {
          fn(err);
        } else if (result) {
          attempts += 1;
          mysql.generateBinId(fn, attempts);
        } else {
          fn(null, id);
        }
      });
    } else {
      fn(new Error("too-many-tries"));
    }
  },
  // getOne('<sql template>', [constraint1, constraint2, ...], fn)
  getOne: function (queryKey, params, fn) {
    this.connection.get(templates[queryKey], params, function (err, result) {
      if (err) {
        fn(err, null);
      } else {
        fn(null, result);
      }
    });
  },
  getUser: function (id, fn) {
    var _this = this;

    this.connection.get(templates.getUser, [id], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (!result) {
        _this.connection.get(templates.getUserByEmail, [id], function (err, result) {
          if (err) {
            return fn(err);
          }

          if (result) {
            result = _this.convertUserDates(result);
          }

          fn(null, result);
        });
      } else {
        if (result) {
          result = _this.convertUserDates(result);
        }
        fn(null, result);
      }
    });
  },
  getUserByApiKey: function (apiKey, fn) {
    var _this = this;

    this.connection.get(templates.getUserByApiKey, [apiKey], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }
      fn(null, result);
    });
  },
  getUserByEmail: function (email, fn) {
    var _this = this;

    this.connection.get(templates.getUserByEmail, [email], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }

      fn(null, result);
    });
  },
  setUser: function (params, fn) {
    var now = new Date(), values = [
      params.name,
      params.key,
      params.email,
      now,
      now,
      now,
      params.github_token,
      params.github_id
    ], sql = templates.setUser;

    this.connection.run(sql, values, function (err) {
      if (err) {
        return fn(err);
      }
      fn(null, this.lastID);
    });
  },
  touchOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.name, params.url, params.revision];

    this.connection.run(templates.touchOwners, values, function (err) {
      if (err) {
        return fn(err);
      }

      if (typeof fn === 'function') {
        fn(null);
      }
    });
  },
  updateOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.summary, params.panel_open, params.name, params.url, params.revision];

    var panel = params.panel,
        allowed = {html: 1, css: 1, javascript: 1},
        sql = templates.updateOwners.replace(':panel', panel);

    if (allowed[panel]) {
      this.connection.run(sql, values, function (err) {
        if (err) {
          return fn(err);
        }

        if (typeof fn === 'function') {
          fn(null);
        }
      });
    } else {
      fn('invalid-panel');
    }
  },
  populateOwners: function (params, fn) {
    // params.date is only for use when populating the summary field
    var values = [params.date || new Date(), params.summary, params.html, params.css, params.javascript, params.name, params.url, params.revision];

    this.connection.run(templates.populateOwners, values, function (err, result) {
      if (err) {
        return fn(err);
      }

      if (typeof fn === 'function') {
        fn(null);
      }
    });
  },
  touchLogin: function (id, fn) {
    var now = new Date();
    this.connection.run(templates.touchLogin, [now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserEmail: function (id, email, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserEmail, [email, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserGithubData: function (id, ghId, token, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserGithubData, [ghId, token, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  updateUserKey: function (id, key, fn) {
    var now = new Date();
    this.connection.run(templates.updateUserKey, [key, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  // Different to updateUserKey() in that it also sets the created timestamp
  // which is required to differentiate between a JSBin 2 user and a new
  // one.
  upgradeUserKey: function (id, key, fn) {
    var now = new Date();
    this.connection.run(templates.upgradeUserKey, [key, now, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  getUserByForgotToken: function (token, fn) {
    var sql = templates.getUserForForgotToken,
        _this = this;

    this.connection.get(sql, [token, new Date()], function (err, result) {
      if (err) {
        return fn(err);
      }

      if (result) {
        result = _this.convertUserDates(result);
      }

      fn(null, result);
    });
  },
  setForgotToken: function (user, token, fn) {
    var sql = templates.setForgotToken,
        expires = this.expireDate(),
        params = [user, token, expires, new Date()];

    this.connection.run(sql, params, function (err) {
      if (err) {
        return fn(err);
      }
      fn();
    });
  },
  expireForgotToken: function (token, fn) {
    var sql = templates.deleteExpiredForgotToken;

    // Allow all old tokens to be expired with same call.
    if (typeof token === 'function') {
      fn = token;
      token = null;
    }

    this.connection.run(sql, [new Date(), token, null], function (err, results) {
      fn(err || null);
    });
  },
  expireForgotTokenByUser: function (user, fn) {
    var sql = templates.deleteExpiredForgotToken;

    this.connection.run(sql, [new Date(), null, user], function (err) {
      fn(err || null);
    });
  },
  expireDate: function () {
    var expires = new Date();
    expires.setUTCDate(expires.getUTCDate() + 1);
    return expires;
  },
  applyBinDefaults: function (bin) {
    for (var prop in this.defaults) {
      if (bin[prop] == null) { // Using == to catch null and undefined.
        bin[prop] = this.defaults[prop];
      }
    }

    this.convertDates(bin, 'last_updated');

    bin.active = bin.active === 'y';

    if (!bin.last_updated || isNaN(bin.last_updated.getTime())) bin.last_updated = new Date('2012-07-23 00:00:00');

    try {
      bin.settings = JSON.parse(bin.settings || '{}');
    } catch (e) {
      // this is likely because the settings were screwed in a beta build
      bin.settings = {};
    }
    // console.log(bin);

    return bin;
  },
  convertUserDates: function (user) {
    return this.convertDates(user, 'created', 'updated', 'last_login');
  },
  convertBinDates: function (bin) {
    return this.convertDates(bin, 'created', 'last_viewed');
  },
  convertDates: function (obj/* keys */) {
    var keys = [].slice.call(arguments, 1);
    keys.forEach(function (key) {
      if (obj && obj[key]) {
        var date = new Date();
        date.setTime(obj[key]);
        obj[key] = date;
      }
    });
    return obj;
  },
  reportBin: function (params, fn) {
    var now = new Date(), values = [
      now,
      params.url,
      params.revision
    ], sql = templates.reportBin;

    this.connection.run(sql, values, function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  },
  archiveBin: function (bin, fn) {
    var values = [bin.archive, bin.name, bin.url, bin.revision],
        sql = templates.archiveBin;

    this.connection.run(sql, values, function (err, result) {
      if (err || !this.changes) {
        return fn(err);
      }
      fn(null, result);
    });
  },
  isOwnerOf: function (params, fn) {
    var values = [
      params.name || '',
      params.url
    ], sql = templates.isOwnerOf;

    // note: .get gets one row
    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }
      if (typeof result === 'undefined') {
        return fn(null, { found: false });
      } else {
        return fn(null, { found: true, isowner: result.owner === 1 });
      }
    });
  },
  getUserBinCount: function (id, fn) {
    var values = [id],
        sql = templates.getUserBinCount;

    this.connection.get(sql, values, function (err, result) {
      if (err) {
        return fn(err);
      }
      if (typeof result === 'undefined') {
        return fn(null, { found: false, total: 0 });
      } else {
        return fn(null, { found: true, total: result.total });
      }
    });
  },
  getBinMetadata: function(bin, fn) {
    var sql = templates.getBinMetadata;
    this.connection.get(sql, [bin.url, bin.revision], function(err, result) {
      if (err) {
        return fn(err);
      }
      fn(null, result[0] ? result[0] : {
        visibility: 'public',
        name: 'anon'
      });
    });
  },
  setBinVisibility: function(bin, name, value, fn) {
    var sql = templates.setBinVisibility, params = [
      value, name, bin.url
    ];
    if (!bin.metadata || bin.metadata.name !== name) {
      return fn(301);
    }
    this.connection.run(sql, params, function(err, result) {
      if (err) {
        return fn(500);
      }
      fn(err, result);
    });
  },
  setProAccount: function(id, pro, fn){
    var now = new Date();
    this.connection.run(templates.setProAccount, [pro, now, id], function (err) {
      if (err) {
        return fn(err);
      }
      fn(null);
    });
  }
});
<|endoftext|>


<commit_before>var util = require('util');
var pg = require('pg');
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var PgDriver = Base.extend({
    init: function(connection) {
        this._super();
        this.connection = connection;
        this.connection.connect();
    },
    startMigration: function(cb){
        this.runSql('BEGIN;', function() { cb()});
    },

    endMigration: function(cb){
        this.runSql('COMMIT;', function(){cb(null)});
    },

    createColumnDef: function(name, spec, options) {
        return [name, spec.autoIncrement ? '' : this.mapDataType(spec.type), this.createColumnConstraint(spec, options)].join(' ');
    },
    mapDataType: function(str) {
        switch(str) {
          case type.DATE_TIME:
            return 'TIMESTAMP';
          case type.BLOG:
            return 'BYTEA';
        }
        return this._super(str);
    },

    createColumnConstraint: function(spec, options) {
        var constraint = [];
        if (spec.primaryKey && options.emitPrimaryKey) {
            if (spec.autoIncrement) {
                constraint.push('SERIAL');
            } else {
                constraint.push('PRIMARY KEY');
            }
        }

        if (spec.notNull) {
            constraint.push('NOT NULL');
        }

        if (spec.unique) {
            constraint.push('UNIQUE');
        }

        if (typeof spec.defaultValue != 'undefined') {
            constraint.push('DEFAULT');
            if (typeof spec.defaultValue == 'string'){
                constraint.push("'" + spec.defaultValue + "'");
            } else {
            constraint.push(spec.defaultValue);
            }
        }

        return constraint.join(' ');
    },

    renameTable: function(tableName, newTableName, callback) {
        var sql = util.format('RENAME TABLE %s TO %s', tableName, newTableName);
        this.runSql(sql, callback);
    },

    removeColumn: function(tableName, columnName, callback) {
        var sql = util.format("ALTER TABLE %s DROP COLUMN %s", tableName, columnName);
        this.runSql(sql, callback);
    },

    renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
        var sql = util.format("ALTER TABLE %s RENAME COLUMN %s TO %s", tableName, oldColumnName, newColumnName);
        this.runSql(sql, callback)
    },

    //changeColumn: function(tableName, columnName, columnSpec, callback) {
    //},

    runSql: function() {
        params = arguments;
        if (params.length > 2){
            // We have parameters, but db-migrate uses "?" for param substitutions.
            // PG uses "$1", "$2", etc so fix up the "?" into "$1", etc
            var param = params[0].split('?'),
                new_param = [];
            for (var i = 0; i < param.length-1; i++){
                new_param.push(param[i], "$" + (i+1));
            }
            new_param.push(param[param.length-1]);
            params[0] = new_param.join('');
        }
        log.info('pg.runSql', params);
        this.connection.query.apply(this.connection, params);
    },

    all: function() {
        params = arguments;
        this.connection.query.apply(this.connection, [params[0], function(err, result){
            params[1](err, result.rows);
        }]);
    },

    close: function() {
        this.connection.end();
    }

});

exports.connect = function(config, callback) {
    var db = config.db || new pg.Client(config);
    callback(null, new PgDriver(db));
};
<commit_msg>Updating primary key DDL<commit_after>var util = require('util');
var pg = require('pg');
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var PgDriver = Base.extend({
    init: function(connection) {
        this._super();
        this.connection = connection;
        this.connection.connect();
    },
    startMigration: function(cb){
        this.runSql('BEGIN;', function() { cb()});
    },

    endMigration: function(cb){
        this.runSql('COMMIT;', function(){cb(null)});
    },

    createColumnDef: function(name, spec, options) {
        return [name, spec.autoIncrement ? '' : this.mapDataType(spec.type), this.createColumnConstraint(spec, options)].join(' ');
    },
    mapDataType: function(str) {
        switch(str) {
          case type.DATE_TIME:
            return 'TIMESTAMP';
          case type.BLOG:
            return 'BYTEA';
        }
        return this._super(str);
    },

    createColumnConstraint: function(spec, options) {
        var constraint = [];
        if (spec.primaryKey && options.emitPrimaryKey) {
            if (spec.autoIncrement) {
                constraint.push('SERIAL');
            }
            constraint.push('PRIMARY KEY');
        }

        if (spec.notNull) {
            constraint.push('NOT NULL');
        }

        if (spec.unique) {
            constraint.push('UNIQUE');
        }

        if (typeof spec.defaultValue != 'undefined') {
            constraint.push('DEFAULT');
            if (typeof spec.defaultValue == 'string'){
                constraint.push("'" + spec.defaultValue + "'");
            } else {
            constraint.push(spec.defaultValue);
            }
        }

        return constraint.join(' ');
    },

    renameTable: function(tableName, newTableName, callback) {
        var sql = util.format('RENAME TABLE %s TO %s', tableName, newTableName);
        this.runSql(sql, callback);
    },

    removeColumn: function(tableName, columnName, callback) {
        var sql = util.format("ALTER TABLE %s DROP COLUMN %s", tableName, columnName);
        this.runSql(sql, callback);
    },

    renameColumn: function(tableName, oldColumnName, newColumnName, callback) {
        var sql = util.format("ALTER TABLE %s RENAME COLUMN %s TO %s", tableName, oldColumnName, newColumnName);
        this.runSql(sql, callback)
    },

    //changeColumn: function(tableName, columnName, columnSpec, callback) {
    //},

    runSql: function() {
        params = arguments;
        if (params.length > 2){
            // We have parameters, but db-migrate uses "?" for param substitutions.
            // PG uses "$1", "$2", etc so fix up the "?" into "$1", etc
            var param = params[0].split('?'),
                new_param = [];
            for (var i = 0; i < param.length-1; i++){
                new_param.push(param[i], "$" + (i+1));
            }
            new_param.push(param[param.length-1]);
            params[0] = new_param.join('');
        }
        log.info('pg.runSql', params);
        this.connection.query.apply(this.connection, params);
    },

    all: function() {
        params = arguments;
        this.connection.query.apply(this.connection, [params[0], function(err, result){
            params[1](err, result.rows);
        }]);
    },

    close: function() {
        this.connection.end();
    }

});

exports.connect = function(config, callback) {
    var db = config.db || new pg.Client(config);
    callback(null, new PgDriver(db));
};
<|endoftext|>