<commit_before>/*
 * grunt-i18n-js
 * https://github.com/Matthias/grunt-i18n-js
 *
 * Copyright (c) 2014 Matthias Leitner
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var _    = require('lodash');
  var walk = require('walk');
  var done = null;
  var translations = {};
  var options = {};

  grunt.registerMultiTask('i18n', 'Convert rails locales to i18n-js compatible js files', function() {
    done = this.async();
    options = this.options({});

    localePaths.call(this);
  });

  function exportLocales(){
    var segments = translationSegments();

    for (var key in segments) {
      if (segments.hasOwnProperty(key)) {
        save(segments[key], key);
      }
    }

    done();
  }

  function save(translationData, filepath){
    var contents = "var I18n = I18n || {}; \n";
    contents += "I18n.translations = ";
    contents += JSON.stringify(translationData);
    contents += ";";

    grunt.log.writeln("Saving to file "+ JSON.stringify(filepath));

    grunt.file.write(filepath, contents);
  }

  function translationSegments(){
    if(validConfigPresent()) {
      return configuredSegments();
    } else {
      return singleFileExport();
    }
  }

  function validConfigPresent(){
    return (configExists() && config()["translations"]);
  }

  function singleFileExport(){
    var segments = {};
    segments[exportDir() + "/translations.js"] = translations;
    return segments;
  }

  function mergeWithFallback(result, locale, scope, fallback){
    var fallbackLoc= fallbackLocale(fallback);
    var fallbackScope = scopeWithLocale(scope, fallbackLoc);
    var fallbackResult = scopedTranslations(fallbackScope)

    if(!result[locale]) {
      result[locale] = {};
    }

    _.merge(fallbackResult[fallbackLoc],result[locale]);
  }

  function scopeWithLocale(scope, locale){
    var flatScope = _.flatten([scope]);
    return _.map(flatScope, function(s) { return locale + "." + s; });
  }

  function scopedTranslations(scopes) {
    var result = {};
    var flatScopes = _.flatten([scopes]);

    flatScopes.forEach(function(scope){
      grunt.log.write("scopedTranslations ");
      grunt.log.writeln(scope);
      grunt.log.writeln(JSON.stringify(filter(translations, scope)));
      _.merge(result, filter(translations, scope));
    });

    return result;
  }

  function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj) {
        if(obj.hasOwnProperty(key)) {
            temp[key] = clone(obj[key]);
        }
    }
    return temp;
  }

  function filter(translationData, scopes) {
    if (typeof scopes == 'string' || scopes instanceof String) {
      scopes = scopes.split(".")
    }

    scopes = clone(scopes);
    var scope = scopes.shift();

    var results = {};

    if (scope === '*') {
      translationData.forEach(function(value, index) {
        var tmp = _.isEmpty(scopes) ? translationData : filter(translationData, scopes);

        if(tmp != null) {
          results[scope] = tmp;
        }
      });
    } else if(translationData[scope]){
      results[scope] = _.isEmpty(scopes) ? translationData[scope] : filter(translationData[scope], scopes);
    };

    return results;
  }

  function fallbackLocale(locale){
    return availableLocales().indexOf(locale) > 0 ? locale : "en";
  }

  function availableLocales(){
    return ["en","de","es","it","nl","fr","cs","ja","pl","pt-br","pt-pt","ru","zh","tr"];
  }

  function segmentsPerLocale(pattern, scope, fallback){
    var segments = {};

    availableLocales().forEach(function(locale){
      var localeScopes = scopeWithLocale(scope, locale);
      var result = scopedTranslations(localeScopes);

      if(fallback) {
        mergeWithFallback(result, locale, scope, fallback);
      }

      if(!_.isEmpty(result)) {
        var segmentName = pattern.replace('%{locale}', locale);
        segments[segmentName] = result;
      }
    });

    return segments;
  }

  function configExists(){
    return grunt.file.exists(configFile());
  }

  function configuredSegments(){
    var segments = {};

    config()["translations"].forEach(function(options){
      _.merge({"only":"*", "fallback": false}, options);

      if (_.include(options["file"], '%{locale}')) {
        var segmentsToMerge = segmentsPerLocale(options["file"],
                                                options["only"],
                                                options["fallback"]);

        _.assign(segments, segmentsToMerge);
      } else {
        var result = segmentForScope(options["only"]);
        var segmentKey = options["file"];

        if(!_.isEmpty(result)){
          segments[segmentKey] = result;
        }
      }
    });

    return segments;
  }

  function localePaths(){
    var files   = [];
    var walker  = walk.walk(localePath(), { followLinks: false });

    walker.on('file', function(root, stat, next) {
      if(_.contains(stat.name, '.yml')){
        files.push(root + '/' + stat.name);
      }
      next();
    });

    walker.on('end', function() {
      readLocaleFiles(files);
      exportLocales();
    });
  }

  function localePath(){
    return options.railsPath + "/app/locales";
  }

  function segmentForScope(scope){
    if (scope === "*") {
      return translations;
    } else {
      return scopedTranslations(scope);
    };
  }

  function configFile(){
    return options.railsPath + "/config/i18n-js.yml";
  }

  function readLocaleFiles(paths){
    paths.forEach(function(path){
      readLocaleFile(path);
    });
  }
  function readLocaleFile(path){
    _.merge(translations, grunt.file.readYAML(path));
  }

  function config(){
    return grunt.file.readYAML(configFile());
  }

  function exportDir(){
    return "public/javascripts";
  }
};
<commit_msg>Fix export path<commit_after>/*
 * grunt-i18n-js
 * https://github.com/Matthias/grunt-i18n-js
 *
 * Copyright (c) 2014 Matthias Leitner
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var _    = require('lodash');
  var walk = require('walk');
  var done = null;
  var translations = {};
  var options = {};

  grunt.registerMultiTask('i18n', 'Convert rails locales to i18n-js compatible js files', function() {
    done = this.async();
    options = this.options({});

    localePaths.call(this);
  });

  function exportLocales(){
    var segments = translationSegments();

    for (var key in segments) {
      if (segments.hasOwnProperty(key)) {
        save(segments[key], key);
      }
    }

    done();
  }

  function save(translationData, filepath){
    filepath = filepath.replace("app/assets/javascripts/", "");

    var contents = "var I18n = I18n || {}; \n";
    contents += "I18n.translations = ";
    contents += JSON.stringify(translationData);
    contents += ";";

    grunt.log.writeln("Saving to file " + filepath);
    grunt.file.write(filepath, contents);
  }

  function translationSegments(){
    if(validConfigPresent()) {
      return configuredSegments();
    } else {
      return singleFileExport();
    }
  }

  function validConfigPresent(){
    return (configExists() && config()["translations"]);
  }

  function singleFileExport(){
    var segments = {};
    segments[exportDir() + "/translations.js"] = translations;
    return segments;
  }

  function mergeWithFallback(result, locale, scope, fallback){
    var fallbackLoc= fallbackLocale(fallback);
    var fallbackScope = scopeWithLocale(scope, fallbackLoc);
    var fallbackResult = scopedTranslations(fallbackScope)

    if(!result[locale]) {
      result[locale] = {};
    }

    _.merge(fallbackResult[fallbackLoc],result[locale]);
  }

  function scopeWithLocale(scope, locale){
    var flatScope = _.flatten([scope]);
    return _.map(flatScope, function(s) { return locale + "." + s; });
  }

  function scopedTranslations(scopes) {
    var result = {};
    var flatScopes = _.flatten([scopes]);

    flatScopes.forEach(function(scope){
      grunt.log.write("scopedTranslations ");
      grunt.log.writeln(scope);
      grunt.log.writeln(JSON.stringify(filter(translations, scope)));
      _.merge(result, filter(translations, scope));
    });

    return result;
  }

  function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj) {
        if(obj.hasOwnProperty(key)) {
            temp[key] = clone(obj[key]);
        }
    }
    return temp;
  }

  function filter(translationData, scopes) {
    if (typeof scopes == 'string' || scopes instanceof String) {
      scopes = scopes.split(".")
    }

    scopes = clone(scopes);
    var scope = scopes.shift();

    var results = {};

    if (scope === '*') {
      translationData.forEach(function(value, index) {
        var tmp = _.isEmpty(scopes) ? translationData : filter(translationData, scopes);

        if(tmp != null) {
          results[scope] = tmp;
        }
      });
    } else if(translationData[scope]){
      results[scope] = _.isEmpty(scopes) ? translationData[scope] : filter(translationData[scope], scopes);
    };

    return results;
  }

  function fallbackLocale(locale){
    return availableLocales().indexOf(locale) > 0 ? locale : "en";
  }

  function availableLocales(){
    return ["en","de","es","it","nl","fr","cs","ja","pl","pt-br","pt-pt","ru","zh","tr"];
  }

  function segmentsPerLocale(pattern, scope, fallback){
    var segments = {};

    availableLocales().forEach(function(locale){
      var localeScopes = scopeWithLocale(scope, locale);
      var result = scopedTranslations(localeScopes);

      if(fallback) {
        mergeWithFallback(result, locale, scope, fallback);
      }

      if(!_.isEmpty(result)) {
        var segmentName = pattern.replace('%{locale}', locale);
        segments[segmentName] = result;
      }
    });

    return segments;
  }

  function configExists(){
    return grunt.file.exists(configFile());
  }

  function configuredSegments(){
    var segments = {};

    config()["translations"].forEach(function(options){
      _.merge({"only":"*", "fallback": false}, options);

      if (_.include(options["file"], '%{locale}')) {
        var segmentsToMerge = segmentsPerLocale(options["file"],
                                                options["only"],
                                                options["fallback"]);

        _.assign(segments, segmentsToMerge);
      } else {
        var result = segmentForScope(options["only"]);
        var segmentKey = options["file"];

        if(!_.isEmpty(result)){
          segments[segmentKey] = result;
        }
      }
    });

    return segments;
  }

  function localePaths(){
    var files   = [];
    var walker  = walk.walk(localePath(), { followLinks: false });

    walker.on('file', function(root, stat, next) {
      if(_.contains(stat.name, '.yml')){
        files.push(root + '/' + stat.name);
      }
      next();
    });

    walker.on('end', function() {
      readLocaleFiles(files);
      exportLocales();
    });
  }

  function localePath(){
    return options.railsPath + "/app/locales";
  }

  function segmentForScope(scope){
    if (scope === "*") {
      return translations;
    } else {
      return scopedTranslations(scope);
    };
  }

  function configFile(){
    return options.railsPath + "/config/i18n-js.yml";
  }

  function readLocaleFiles(paths){
    paths.forEach(function(path){
      readLocaleFile(path);
    });
  }
  function readLocaleFile(path){
    _.merge(translations, grunt.file.readYAML(path));
  }

  function config(){
    return grunt.file.readYAML(configFile());
  }

  function exportDir(){
    return "public/javascripts";
  }
};
<|endoftext|>


<commit_before>var request = require('request');
var fs = require('fs');
var _ = require('underscore');
var q = require('q');
/*
 * grunt-jenkins
 * https://github.com/sghill/grunt-jenkins
 *
 * Copyright (c) 2012 sghill
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  grunt.config.requires('jenkins.serverAddress');

  var SERVER = grunt.config('jenkins.serverAddress');
  var PIPELINE_DIRECTORY = grunt.config('jenkins.pipelineDirectory') || 'pipeline';

  function logError(e) {
    grunt.log.error(e);
  }

  function isLast(item, collection) {
    return _.isEqual(item, _.last(collection));
  }

  function withDot(filename) {
    return (/\./).test(filename);
  }

  function loadJobsFromDisk() {
    var deferred = q.defer();
    fs.readdir(PIPELINE_DIRECTORY, function(e, contents) {
      if(e) { return deferred.reject(e); }
      // assumption: we don't have periods in our job names
      var directories = _.reject(contents, withDot);
      deferred.resolve(directories);
    });
    return deferred.promise;
  }

  function fetchJobConfigurationStrategy(job) {
    var deferred = q.defer();
    var url = [SERVER, 'job', job, 'config.xml'].join('/');
    request(url, function(e, r, b) {
      var strategy = r.statusCode === 200 ? 'update' : 'create';
      grunt.log.writeln(strategy + ': ' + job);
      deferred.resolve({strategy: strategy, jobName: job});
    });
    return deferred.promise;
  }

  function fetchFileContents (fileAndJob) {
    var deferred = q.defer();
    fs.readFile(fileAndJob.fileName, function(e, contents) {
      if(e) { return deferred.reject(e); }
      deferred.resolve({fileContents: contents, jobName: fileAndJob.jobName });
    });
    return deferred.promise;
  }

  function createJob (config) {
    var deferred = q.defer();
    var options = {
      url: [SERVER, 'createItem'].join('/'),
      method: 'POST',
      qs: {
        name: config.jobName
      },
      headers: {
        'Content-Type': 'text/xml'
      },
      body: config.fileContents
    };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function updateJob (config) {
    var deferred = q.defer(),
        options = {
      url: [SERVER, 'job', config.jobName, 'config.xml'].join('/'),
      method: 'POST',
      body: config.fileContents
    };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function applyStrategy (strategyObj) {
    var deferred = q.defer(),
        filename = [PIPELINE_DIRECTORY, strategyObj.jobName, 'config.xml'].join('/'),
        fileStrategy = {fileName: filename, jobName: strategyObj.jobName};

    function resolve (val) {
      deferred.resolve(val);
    }

    if(strategyObj.strategy === 'create') {
      fetchFileContents(fileStrategy).
        then(createJob).
        then(resolve);
    } else if (strategyObj.strategy === 'update') {
      fetchFileContents(fileStrategy).
        then(updateJob).
        then(resolve);
    }

    return deferred.promise;
  }

  function createOrUpdateJobs(directories) {
    var deferred = q.defer();

    function resolve (val) {
      deferred.resolve(val);
    }

    _.each(directories, function(folder) {
      fetchJobConfigurationStrategy(folder).
        then(applyStrategy).
        then(resolve);
    });

    return deferred.promise;
  }

  function loadPluginsFromDisk() {
    var deferred = q.defer();
    var filename = [PIPELINE_DIRECTORY, 'plugins.json'].join('/');
    fs.readFile(filename, function(e, contents) {
      if(e || _.isUndefined(contents)) { return deferred.reject(e); }
      deferred.resolve(JSON.parse(contents));
    });
    return deferred.promise;
  }

  function transformToJenkinsXml(plugins) {
    var attributes = _.map(plugins, function(p) {
      return ['<install plugin="', p.id, '@', p.version, '" />'].join('');
    }).join('\n');
    return ['<jenkins>', attributes, '</jenkins>'].join('\n');
  }

  function installPlugins(xml) {
    var deferred = q.defer(),
        options = {
          url: [SERVER, 'pluginManager', 'installNecessaryPlugins'].join('/'),
          method: 'POST',
          body: xml
        };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function fetchEnabledPluginsFromServer() {
    var url = [SERVER, 'pluginManager', 'api', 'json?depth=1'].join('/');
    var deferred = q.defer();

    request(url, function(e, r, body) {
      var result = _.filter(JSON.parse(body).plugins, function(p) { return p.enabled; });
      var plugins = _.map(result, function(p) { return { id: p.shortName, version: p.version }; });

      deferred.resolve(plugins);
    });

    return deferred.promise;
  }

  function ensureDirectoriesExist(directories) {
    _.each(directories, function(d, index) {
      var path = _.take(directories, (index + 1)).join('/');
      if(!fs.existsSync(path)) {
        fs.mkdirSync(path);
      }
    });
  }

  function writeFileToPipelineDirectory(plugins) {
    var deferred = q.defer();
    ensureDirectoriesExist([PIPELINE_DIRECTORY]);
    var filename = [PIPELINE_DIRECTORY, 'plugins.json'].join('/');
    var body = JSON.stringify(plugins, null, 2);
    fs.writeFile(filename, body, 'utf8', function(e) {
      if(e) { return deferred.reject(e); }

      grunt.log.ok('created file: ' + filename);
      deferred.resolve();
    });

    return deferred.promise;
  }

  function fetchJobsFromServer() {
    var deferred = q.defer();
    var url = [SERVER, 'api', 'json'].join('/');

    request(url, function(e, r, body) {
      if(e) { return deferred.reject(e); }
      var jobs = JSON.parse(body).jobs;
      deferred.resolve(_.map(jobs, function(j) { return { name: j.name, url: j.url }; }));
    });

    return deferred.promise;
  }

  function fetchJobConfigurations(jobs) {
    var deferred = q.defer();
    var jobConfigurations = [];
    var errors = [];
    var responseCodes = [];
    _.each(jobs, function(j) {
      request([j.url, 'config.xml'].join(''), function(e, r, body) {
        errors.push(e);
        responseCodes.push(r.statusCode);
        j.config = body;
        jobConfigurations.push(j);
        if(isLast(j, jobs)) {
          if(_.any(errors, function(e) { return e; }) ||
             _.any(responseCodes, function(c) { return c !== 200; })) {
            return deferred.reject();
          }
          deferred.resolve(jobConfigurations);
        }
      });
    });
    return deferred.promise;
  }

  function writeToJobDirectories(jobs) {
    var deferred = q.defer();
    _.each(jobs, function(j) {
      ensureDirectoriesExist([PIPELINE_DIRECTORY, j.name]);
      var filename = [PIPELINE_DIRECTORY, j.name, 'config.xml'].join('/');

      fs.writeFile(filename, j.config, 'utf8', function(e) {
        if(e) { return deferred.reject(e); }

        grunt.log.ok('created file: ' + filename);
        if(isLast(j, jobs)) {
          deferred.resolve(jobs);
        }
      });
    });
    return deferred.promise;
  }

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerTask('jenkins-install', 'jenkins-install-jobs jenkins-install-plugins');
  grunt.registerTask('jenkins-backup', 'jenkins-backup-jobs jenkins-backup-plugins');

  grunt.registerTask('jenkins-install-jobs', 'install all Jenkins jobs', function() {
    var done = this.async();
    loadJobsFromDisk().
      then(createOrUpdateJobs).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-install-plugins', 'install all Jenkins plugins', function() {
    var done = this.async();
    loadPluginsFromDisk().
      then(transformToJenkinsXml).
      then(installPlugins).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-backup-jobs', 'backup all Jenkins jobs', function() {
    var done = this.async();
    fetchJobsFromServer().
      then(fetchJobConfigurations).
      then(writeToJobDirectories).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-backup-plugins', 'backup all enabled Jenkins plugins', function() {
    var done = this.async();
    fetchEnabledPluginsFromServer().
      then(writeFileToPipelineDirectory).
      then(function() { done(true); }, logError);
  });

};
<commit_msg>added list-projects and list-jobs<commit_after>var request = require('request');
var fs = require('fs');
var _ = require('underscore');
var q = require('q');
/*
 * grunt-jenkins
 * https://github.com/sghill/grunt-jenkins
 *
 * Copyright (c) 2012 sghill
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  grunt.config.requires('jenkins.serverAddress');

  var SERVER = grunt.config('jenkins.serverAddress');
  var PIPELINE_DIRECTORY = grunt.config('jenkins.pipelineDirectory') || 'pipeline';

  function logError(e) {
    grunt.log.error(e);
  }

  function isLast(item, collection) {
    return _.isEqual(item, _.last(collection));
  }

  function withDot(filename) {
    return (/\./).test(filename);
  }

  function loadJobsFromDisk() {
    var deferred = q.defer();
    fs.readdir(PIPELINE_DIRECTORY, function(e, contents) {
      if(e) { return deferred.reject(e); }
      // assumption: we don't have periods in our job names
      var directories = _.reject(contents, withDot);
      deferred.resolve(directories);
    });
    return deferred.promise;
  }

  function fetchJobConfigurationStrategy(job) {
    var deferred = q.defer();
    var url = [SERVER, 'job', job, 'config.xml'].join('/');
    request(url, function(e, r, b) {
      var strategy = r.statusCode === 200 ? 'update' : 'create';
      grunt.log.writeln(strategy + ': ' + job);
      deferred.resolve({strategy: strategy, jobName: job});
    });
    return deferred.promise;
  }

  function fetchFileContents (fileAndJob) {
    var deferred = q.defer();
    fs.readFile(fileAndJob.fileName, function(e, contents) {
      if(e) { return deferred.reject(e); }
      deferred.resolve({fileContents: contents, jobName: fileAndJob.jobName });
    });
    return deferred.promise;
  }

  function createJob (config) {
    var deferred = q.defer();
    var options = {
      url: [SERVER, 'createItem'].join('/'),
      method: 'POST',
      qs: {
        name: config.jobName
      },
      headers: {
        'Content-Type': 'text/xml'
      },
      body: config.fileContents
    };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function updateJob (config) {
    var deferred = q.defer(),
        options = {
      url: [SERVER, 'job', config.jobName, 'config.xml'].join('/'),
      method: 'POST',
      body: config.fileContents
    };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function applyStrategy (strategyObj) {
    var deferred = q.defer(),
        filename = [PIPELINE_DIRECTORY, strategyObj.jobName, 'config.xml'].join('/'),
        fileStrategy = {fileName: filename, jobName: strategyObj.jobName};

    function resolve (val) {
      deferred.resolve(val);
    }

    if(strategyObj.strategy === 'create') {
      fetchFileContents(fileStrategy).
        then(createJob).
        then(resolve);
    } else if (strategyObj.strategy === 'update') {
      fetchFileContents(fileStrategy).
        then(updateJob).
        then(resolve);
    }

    return deferred.promise;
  }

  function createOrUpdateJobs(directories) {
    var deferred = q.defer();

    function resolve (val) {
      deferred.resolve(val);
    }

    _.each(directories, function(folder) {
      fetchJobConfigurationStrategy(folder).
        then(applyStrategy).
        then(resolve);
    });

    return deferred.promise;
  }

  function loadPluginsFromDisk() {
    var deferred = q.defer();
    var filename = [PIPELINE_DIRECTORY, 'plugins.json'].join('/');
    fs.readFile(filename, function(e, contents) {
      if(e || _.isUndefined(contents)) { return deferred.reject(e); }
      deferred.resolve(JSON.parse(contents));
    });
    return deferred.promise;
  }

  function transformToJenkinsXml(plugins) {
    var attributes = _.map(plugins, function(p) {
      return ['<install plugin="', p.id, '@', p.version, '" />'].join('');
    }).join('\n');
    return ['<jenkins>', attributes, '</jenkins>'].join('\n');
  }

  function installPlugins(xml) {
    var deferred = q.defer(),
        options = {
          url: [SERVER, 'pluginManager', 'installNecessaryPlugins'].join('/'),
          method: 'POST',
          body: xml
        };

    request(options, function(e, r, b) {
      if(e) { return deferred.reject(e); }
      deferred.resolve(r.statusCode === 200);
    });

    return deferred.promise;
  }

  function fetchEnabledPluginsFromServer() {
    var url = [SERVER, 'pluginManager', 'api', 'json?depth=1'].join('/');
    var deferred = q.defer();

    request(url, function(e, r, body) {
      var result = _.filter(JSON.parse(body).plugins, function(p) { return p.enabled; });
      var plugins = _.map(result, function(p) { return { id: p.shortName, version: p.version }; });

      deferred.resolve(plugins);
    });

    return deferred.promise;
  }

  function ensureDirectoriesExist(directories) {
    _.each(directories, function(d, index) {
      var path = _.take(directories, (index + 1)).join('/');
      if(!fs.existsSync(path)) {
        fs.mkdirSync(path);
      }
    });
  }

  function writeFileToPipelineDirectory(plugins) {
    var deferred = q.defer();
    ensureDirectoriesExist([PIPELINE_DIRECTORY]);
    var filename = [PIPELINE_DIRECTORY, 'plugins.json'].join('/');
    var body = JSON.stringify(plugins, null, 2);
    fs.writeFile(filename, body, 'utf8', function(e) {
      if(e) { return deferred.reject(e); }

      grunt.log.ok('created file: ' + filename);
      deferred.resolve();
    });

    return deferred.promise;
  }

  function fetchJobsFromServer() {
    var deferred = q.defer();
    var url = [SERVER, 'api', 'json'].join('/');

    request(url, function(e, r, body) {
      if(e) { return deferred.reject(e); }
      var jobs = JSON.parse(body).jobs;
      deferred.resolve(_.map(jobs, function(j) { return { name: j.name, url: j.url }; }));
    });

    return deferred.promise;
  }

  function fetchJobConfigurations(jobs) {
    var deferred = q.defer();
    var jobConfigurations = [];
    var errors = [];
    var responseCodes = [];
    _.each(jobs, function(j) {
      request([j.url, 'config.xml'].join(''), function(e, r, body) {
        errors.push(e);
        responseCodes.push(r.statusCode);
        j.config = body;
        jobConfigurations.push(j);
        if(isLast(j, jobs)) {
          if(_.any(errors, function(e) { return e; }) ||
             _.any(responseCodes, function(c) { return c !== 200; })) {
            return deferred.reject();
          }
          deferred.resolve(jobConfigurations);
        }
      });
    });
    return deferred.promise;
  }

  function writeToJobDirectories(jobs) {
    var deferred = q.defer();
    _.each(jobs, function(j) {
      ensureDirectoriesExist([PIPELINE_DIRECTORY, j.name]);
      var filename = [PIPELINE_DIRECTORY, j.name, 'config.xml'].join('/');

      fs.writeFile(filename, j.config, 'utf8', function(e) {
        if(e) { return deferred.reject(e); }

        grunt.log.ok('created file: ' + filename);
        if(isLast(j, jobs)) {
          deferred.resolve(jobs);
        }
      });
    });
    return deferred.promise;
  }

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerTask('jenkins-install', 'jenkins-install-jobs jenkins-install-plugins');
  grunt.registerTask('jenkins-backup', 'jenkins-backup-jobs jenkins-backup-plugins');
  grunt.registerTask('jenkins-list', 'jenkins-list-jobs jenkins-list-plugins');

  grunt.registerTask('jenkins-install-jobs', 'install all Jenkins jobs', function() {
    var done = this.async();
    loadJobsFromDisk().
      then(createOrUpdateJobs).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-install-plugins', 'install all Jenkins plugins', function() {
    var done = this.async();
    loadPluginsFromDisk().
      then(transformToJenkinsXml).
      then(installPlugins).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-backup-jobs', 'backup all Jenkins jobs', function() {
    var done = this.async();
    fetchJobsFromServer().
      then(fetchJobConfigurations).
      then(writeToJobDirectories).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-backup-plugins', 'backup all enabled Jenkins plugins', function() {
    var done = this.async();
    fetchEnabledPluginsFromServer().
      then(writeFileToPipelineDirectory).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-list-jobs', 'list all found Jenkins jobs', function() {
    var done = this.async();
    fetchJobsFromServer().
      then(function(jobs) {
        _.each(jobs, function(j) {
          grunt.log.writeln('job: ' + j.name + ' @ ' + j.url);
        });
      }).
      then(function() { done(true); }, logError);
  });

  grunt.registerTask('jenkins-list-plugins', 'list all enabled Jenkins plugins', function() {
    var done = this.async();
    fetchEnabledPluginsFromServer().
      then(function(plugins) {
        _.each(plugins, function(p) {
          grunt.log.writeln('plugin id: ' + p.id + ', version: ' + p.version);
        });
      }).
      then(function() { done(true); }, logError);
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    sha256 = require("sha256"),
    fs = require("fs"),
    knox = require("knox"),
    crypto = require('crypto'),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    querystring = require('querystring'),
    mkdirp = require('mkdirp'),
    _ = require('lodash');

var HTTP_OK = 200,
    HTTP_NOT_MODIFIED = 304;

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this;

    function createPath(path, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      mkdirp(path, options, function (error, created) {
        if (error) {
          grunt.log.error("Couldn't create %j: %s", path, error);
        }
        callback(error, created);
      });
    }

    function castToArray(thing) {
      if (!thing) {
        return [ ];
      }
      if (Array.isArray(thing)) {
        return thing;
      }
      return [ thing ];
    }

    function formatArgsPrefix(prefix) {
      return prefix ?
        [ "%j: ", prefix ] :
        [ "" ];
    }

    function responseOk(requestName, error, response, allowExtraStatusCodes) {
      var allowStatusCodes = [ HTTP_OK ].concat(castToArray(allowExtraStatusCodes)),
        formatArgs = formatArgsPrefix(requestName),
        ok = true;

      if (error) {
        formatArgs[0] += "Request error: %s";
        formatArgs.push(error);
        ok = false;
      } else if (!response) {
        formatArgs[0] += "No response";
        ok = false;
      } else if (allowStatusCodes.indexOf(response.statusCode) < 0) {
        formatArgs[0] += "Unexpected status code: %s";
        formatArgs.push(response.statusCode);
        ok = false;
      }

      if (!ok) {
        grunt.log.error.apply(grunt.log, formatArgs);
      }
      return ok;
    }

    function tryParseJson(requestName, text) {
      try {
        return JSON.parse(text);
      } catch (error) {
        var formatArgs = formatArgsPrefix(requestName);
        formatArgs[0] += "Error parsing JSON: %s";
        formatArgs.push(error);
        grunt.log.error.apply(grunt.log, formatArgs);
        return null;
      }
    }

    function makeClient( options ) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket', 'secure', 'headers', 'style'
      ]));
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {

      var objQueue = [ obj ],
        prefixQueue = [ "[Root]" ],
        remotePaths = { };

      while (objQueue.length > 0) {
        var thing = objQueue.shift(),
          prefix = prefixQueue.shift();

        // don't need to type check here, only objects and arrays will iterate,
        // which is what we want
        _.forOwn(thing, function (value, key) {
          var keyPath = prefix + "." + key;
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              // rewrite the property in the JSON with the local path
              thing[key] = path.join(mediaCwd, assetPath);

              grunt.verbose.writeln(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath, value, thing[key]
              );

              // record the remote path relative to its root downloading
              // setting it as an object property means dupes are naturally
              // eliminated
              remotePaths[assetPath] = true;
            }
          } else {
            objQueue.push(value);
            prefixQueue.push(keyPath);
          }
        });
      }

      return Object.keys(remotePaths);
    }

    function requestJson(url, options, done) {
      grunt.verbose.writeln("Downloading JSON");

      request(url, function( error, response, body ) {
        var jsonContent;
        if (responseOk(url, error, response)) {
          jsonContent = tryParseJson(body);
        }

        if (jsonContent && options.out) {
          if( options.mediaOut != null ) {

            saveMedia(options, jsonContent, done);

          } else {

            writeJsonFile( options.out, jsonContent );
            done();
            return;

          }
        }

        // we reach here if request failed, json parse failed, or if options.out
        // is not set
        done();
      });

    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(url, options, done) {

      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      createPath(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          done();
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        request(url, function(error, response, body) {

          if (responseOk(url, error, response)) {
            var unzipper = new DecompressZip(zipPath);

            unzipper.on("extract", function (log) {

              // TODO: this should be debug, but take a pass over the whole
              // code and decide what should be debug
              grunt.verbose.writeln("%s extract log:\n%j", zipPath, log);

              //on extraction of the zip, check if mediaOut is set, if so, loop through all the unzipped files, and grab down the neccesary media.
              if(options.mediaOut == null) {
                done();
                return;
              }

              for(var key in log) {
                var unzippedFile = path.join(options.zipOut, log[key].deflated);

                fs.readFile( unzippedFile, function ( err, data ) {
                  if (err) {
                    grunt.log.error("Error reading %j: %s", unzippedFile, err);
                  } else {
                    var body = tryParseJson(data);
                    if (body) {
                      // make a new copy of options, with out set to match
                      // zipOut, as json files get written to options.out
                      var newOptions = _.merge({}, options, { out: unzippedFile });

                      saveMedia(newOptions, body, done);
                      return;
                    }
                  }
                  done();
                });

              }

            });

            unzipper.on("error", function(error) {
              grunt.log.error("Error unzipping file %j: %s", zipPath, error);
              done();
            });

            unzipper.extract({
              path: options.zipOut
            });
          } else {
            done();
          }

        }).pipe(fs.createWriteStream(zipPath));

      });

    }

    function saveMedia(options, body, done) {

      var mediaAssets = getMediaAssets( body, options.mediaCwd );
      var client = makeClient( options.aws );

      grunt.verbose.writeln("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.verbose.writeln("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          writeJsonFile( options.out, body );
          done();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( client, mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( client, remotePath, mediaOut, doneCallback ) {

      var localPath = path.join(mediaOut, remotePath);
      var localHash = null;

      grunt.verbose.writeln("Verifying %j -> %j", remotePath, localPath);
      fs.readFile( localPath, function ( err, data ) {
        if (err) {
          grunt.verbose.error("Etag calculation of local file failed: %s", err);
        } else {
          localHash = crypto.createHash('md5').update(data).digest('hex');
        }

        createPath(path.dirname(localPath), function (error) {
          if (!error) {
            downloadFile(client, remotePath, localPath, localHash, doneCallback);
          }
        });
      });

    }

    function writeJsonFile(out, body) {
      grunt.log.subhead( "Retrieving content..." );
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(body, null, '\t'));

    }

    function downloadFile(client, src, dest, etag, doneCallback) {
      var requestHeaders = { };

      grunt.verbose.writeln("S3 GET %j", src);

      if (etag) {
        grunt.verbose.writeln("If-None-Match: %s", etag);
        requestHeaders["If-None-Match"] = etag;
      }

      client.getFile(src, requestHeaders, function (err, res) {
        if (responseOk(src, err, res, HTTP_NOT_MODIFIED)) {
          var file = fs.createWriteStream(dest);
          file.on("error", function(e) {
            logError("Error writing: %s", e);
            doneCallback();
          });

          res
            .on('error', function (err) {
              logError("Error reading %j: %s", src, err);
              doneCallback();
            })
            .on('end', function () {
              logOk( "downloaded" );
              doneCallback();
            });

          res.pipe(file);
        } else {
          doneCallback();
        }
      });

    }

    function requestFiles(options, gruntCallback) {
      grunt.log.subhead( "Retrieving files..." );
      var url = createApiUrl(options, createRequestId());
      grunt.verbose.writeln("Url is %j", url);

      if (isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(url, options, gruntCallback);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(url, options, gruntCallback);
      }
    }

    function isZipQuery(query) {
      return query.indexOf("dump/zip/") === 0;
    }

    function normalizeDir(path) {
      // TODO: change path string operations to use path lib, and make this
      // function unnecessary
      if (path == null) {
        return null;
      }
      path = String(path);
      if (path === "") {
        return "./";
      }
      if (path.slice(-1) !== "/") {
        return path + "/";
      }
      return path;
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];

      if (isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      options.mediaOut = normalizeDir(options.mediaOut);
      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      } else {
        options.mediaCwd = normalizeDir(options.mediaCwd);
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: messages.length === 0
      };
    }

    function createRequestId() {
      return Date.now().toString(36) +
        (Math.random() * 9007199254740992).toString(36);
    }

    function createToken(secret, key, requestId) {
      return sha256("" + secret + key + requestId);
    }

    function createApiUrl(options, requestId) {
      var url = [
          options.https ? "https" : "http",
          "://",
          options.domain,
          "/api/v",
          options.api,
          "/",
          options.query
        ].join("");

      var queryParams = _.merge({
          requestId: requestId,
          token: createToken(options.secret, options.key, requestId)
        }, options.params || {});

      return url + "?" + querystring.stringify(queryParams);
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");
      var done = thisTask.async();

      if (optionResult.ok) {
        grunt.log.writeln(messagesString);
        requestFiles(optionResult.options, done);
      } else {
        grunt.log.error(messagesString);
        done(false);
      }

    }

    return main();
  });
};
<commit_msg>name callbacks more clearly so the grunt task callback can be distinguished from intermediate callbacks<commit_after>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    sha256 = require("sha256"),
    fs = require("fs"),
    knox = require("knox"),
    crypto = require('crypto'),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    querystring = require('querystring'),
    mkdirp = require('mkdirp'),
    _ = require('lodash');

var HTTP_OK = 200,
    HTTP_NOT_MODIFIED = 304;

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this;
    var gruntFinishTask = this.async();

    function createPath(path, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      mkdirp(path, options, function (error, created) {
        if (error) {
          grunt.log.error("Couldn't create %j: %s", path, error);
        }
        callback(error, created);
      });
    }

    function castToArray(thing) {
      if (!thing) {
        return [ ];
      }
      if (Array.isArray(thing)) {
        return thing;
      }
      return [ thing ];
    }

    function formatArgsPrefix(prefix) {
      return prefix ?
        [ "%j: ", prefix ] :
        [ "" ];
    }

    function responseOk(requestName, error, response, allowExtraStatusCodes) {
      var allowStatusCodes = [ HTTP_OK ].concat(castToArray(allowExtraStatusCodes)),
        formatArgs = formatArgsPrefix(requestName),
        ok = true;

      if (error) {
        formatArgs[0] += "Request error: %s";
        formatArgs.push(error);
        ok = false;
      } else if (!response) {
        formatArgs[0] += "No response";
        ok = false;
      } else if (allowStatusCodes.indexOf(response.statusCode) < 0) {
        formatArgs[0] += "Unexpected status code: %s";
        formatArgs.push(response.statusCode);
        ok = false;
      }

      if (!ok) {
        grunt.log.error.apply(grunt.log, formatArgs);
      }
      return ok;
    }

    function tryParseJson(requestName, text) {
      try {
        return JSON.parse(text);
      } catch (error) {
        var formatArgs = formatArgsPrefix(requestName);
        formatArgs[0] += "Error parsing JSON: %s";
        formatArgs.push(error);
        grunt.log.error.apply(grunt.log, formatArgs);
        return null;
      }
    }

    function makeClient( options ) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket', 'secure', 'headers', 'style'
      ]));
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {

      var objQueue = [ obj ],
        prefixQueue = [ "[Root]" ],
        remotePaths = { };

      while (objQueue.length > 0) {
        var thing = objQueue.shift(),
          prefix = prefixQueue.shift();

        // don't need to type check here, only objects and arrays will iterate,
        // which is what we want
        _.forOwn(thing, function (value, key) {
          var keyPath = prefix + "." + key;
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              // rewrite the property in the JSON with the local path
              thing[key] = path.join(mediaCwd, assetPath);

              grunt.verbose.writeln(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath, value, thing[key]
              );

              // record the remote path relative to its root downloading
              // setting it as an object property means dupes are naturally
              // eliminated
              remotePaths[assetPath] = true;
            }
          } else {
            objQueue.push(value);
            prefixQueue.push(keyPath);
          }
        });
      }

      return Object.keys(remotePaths);
    }

    function requestJson(url, options) {
      grunt.verbose.writeln("Downloading JSON");

      request(url, function( error, response, body ) {
        var jsonContent;
        if (responseOk(url, error, response)) {
          jsonContent = tryParseJson(body);
        }

        if (jsonContent && options.out) {
          if( options.mediaOut != null ) {

            saveMedia(options, jsonContent);

          } else {

            writeJsonFile( options.out, jsonContent );
            gruntFinishTask();
            return;

          }
        }

        // we reach here if request failed, json parse failed, or if options.out
        // is not set
        gruntFinishTask();
      });

    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(url, options) {

      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      createPath(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          gruntFinishTask(false);
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        request(url, function(error, response, body) {

          if (responseOk(url, error, response)) {
            var unzipper = new DecompressZip(zipPath);

            unzipper.on("extract", function (log) {

              // TODO: this should be debug, but take a pass over the whole
              // code and decide what should be debug
              grunt.verbose.writeln("%s extract log:\n%j", zipPath, log);

              //on extraction of the zip, check if mediaOut is set, if so, loop through all the unzipped files, and grab down the neccesary media.
              if(options.mediaOut == null) {
                // if not, we're done
                gruntFinishTask();
                return;
              }

              for(var key in log) {
                var unzippedFile = path.join(options.zipOut, log[key].deflated);

                fs.readFile( unzippedFile, function ( err, data ) {
                  if (err) {
                    grunt.log.error("Error reading %j: %s", unzippedFile, err);
                  } else {
                    var body = tryParseJson(data);
                    if (body) {
                      // make a new copy of options, with out set to match
                      // zipOut, as json files get written to options.out
                      var newOptions = _.merge({}, options, { out: unzippedFile });

                      saveMedia(newOptions, body);
                      return;
                    }
                  }
                });

              }

            });

            unzipper.on("error", function(error) {
              grunt.log.error("Error unzipping file %j: %s", zipPath, error);
              gruntFinishTask(false);
            });

            unzipper.extract({
              path: options.zipOut
            });
          } else {
            gruntFinishTask(false);
          }

        }).pipe(fs.createWriteStream(zipPath));

      });

    }

    function saveMedia(options, body) {

      var mediaAssets = getMediaAssets( body, options.mediaCwd );
      var client = makeClient( options.aws );

      grunt.verbose.writeln("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.verbose.writeln("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          writeJsonFile( options.out, body );
          gruntFinishTask();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( client, mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( client, remotePath, mediaOut, callback ) {

      var localPath = path.join(mediaOut, remotePath);
      var localHash = null;

      grunt.verbose.writeln("Verifying %j -> %j", remotePath, localPath);
      fs.readFile( localPath, function ( err, data ) {
        if (err) {
          grunt.verbose.error("Etag calculation of local file failed: %s", err);
        } else {
          localHash = crypto.createHash('md5').update(data).digest('hex');
        }

        createPath(path.dirname(localPath), function (error) {
          if (!error) {
            downloadFile(client, remotePath, localPath, localHash, callback);
          }
        });
      });

    }

    function writeJsonFile(out, body) {
      grunt.log.subhead( "Retrieving content..." );
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(body, null, '\t'));

    }

    function downloadFile(client, src, dest, etag, callback) {
      var requestHeaders = { };

      grunt.verbose.writeln("S3 GET %j", src);

      if (etag) {
        grunt.verbose.writeln("If-None-Match: %s", etag);
        requestHeaders["If-None-Match"] = etag;
      }

      client.getFile(src, requestHeaders, function (err, res) {
        if (responseOk(src, err, res, HTTP_NOT_MODIFIED)) {
          var file = fs.createWriteStream(dest);
          file.on("error", function(e) {
            logError("Error writing: %s", e);
            callback();
          });

          res
            .on('error', function (err) {
              logError("Error reading %j: %s", src, err);
              callback();
            })
            .on('end', function () {
              logOk( "downloaded" );
              callback();
            });

          res.pipe(file);
        } else {
          callback();
        }
      });

    }

    function requestFiles(options) {
      grunt.log.subhead( "Retrieving files..." );
      var url = createApiUrl(options, createRequestId());
      grunt.verbose.writeln("Url is %j", url);

      if (isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(url, options);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(url, options);
      }
    }

    function isZipQuery(query) {
      return query.indexOf("dump/zip/") === 0;
    }

    function normalizeDir(path) {
      // TODO: change path string operations to use path lib, and make this
      // function unnecessary
      if (path == null) {
        return null;
      }
      path = String(path);
      if (path === "") {
        return "./";
      }
      if (path.slice(-1) !== "/") {
        return path + "/";
      }
      return path;
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];

      if (isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      options.mediaOut = normalizeDir(options.mediaOut);
      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      } else {
        options.mediaCwd = normalizeDir(options.mediaCwd);
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: messages.length === 0
      };
    }

    function createRequestId() {
      return Date.now().toString(36) +
        (Math.random() * 9007199254740992).toString(36);
    }

    function createToken(secret, key, requestId) {
      return sha256("" + secret + key + requestId);
    }

    function createApiUrl(options, requestId) {
      var url = [
          options.https ? "https" : "http",
          "://",
          options.domain,
          "/api/v",
          options.api,
          "/",
          options.query
        ].join("");

      var queryParams = _.merge({
          requestId: requestId,
          token: createToken(options.secret, options.key, requestId)
        }, options.params || {});

      return url + "?" + querystring.stringify(queryParams);
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");

      if (optionResult.ok) {
        grunt.log.writeln(messagesString);
        requestFiles(optionResult.options);
      } else {
        grunt.log.error(messagesString);
        gruntFinishTask(false);
      }

    }

    return main();
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-vrunner
 * http://vrest.io
 *
 * Copyright (c) 2015 vREST Team
 * Licensed under the MIT license.
 */

'use strict';

var vrunner = require('vrunner'), chalk = require('chalk');

module.exports = function(grunt) {

  //add right side padding
  var rpad = function(str, totalSize, padChar){
    if(!padChar) padChar = ' ';
    if(str.length >= totalSize) return str;
    else {
      for(var i = str.length; i < totalSize; i++){
        str += padChar;
      }
      return str;
    }
  };

  var getMethodName = function(method){
    if(method === "GET") return chalk.blue.bold(method) + '    ';
    else if(method === "POST") return chalk.bold(method) + '   ';
    else if(method === "PUT") return chalk.green.bold(method) + '    ';
    else if(method === "PATCH") return chalk.magenta.bold(method) + '  ';
    else if(method === "DELETE") return chalk.red.bold(method) + ' ';
    else return chalk.bold(method) + ' ';
  };

  grunt.registerTask('vrunner', 'Runs vREST test cases.', function() {
    var done = this.async();
    var over = function(err,dontExit){
      if(!err) err = 'An unknown glitch found.';
      if(!Array.isArray(err)) err = [err];
      err.forEach(function(error){
        console.log(error);
      });
      if(!dontExit) done();
    };
    var options = this.options();
    var Runner = (new vrunner(options));
    Runner.run(function(err,report,remarks){
      if(err) over(err);
      else {
        //grunt.log.ok('EXECUTION OF ALL TEST CASES SUCCESSFULLY COMPLETED.');
        console.log(remarks,report);
        done();
      }
    });

    var index = 1;
    Runner.on('testcase',function(pass,tc,trtc){
      //console.log(trtc.);
      var prefix = rpad(index + '.', 5) + getMethodName(tc.method);
      index++;
      if(pass){
        grunt.log.writeln(prefix + chalk.green.bold(tc.summary || tc.url) + ' (' + trtc.executionTime + 'ms) ');
      } else if(pass === false){
        grunt.log.writeln(prefix + chalk.red.bold(tc.summary || tc.url) + ' (' + trtc.executionTime + 'ms) ');
      } else {
        grunt.log.writeln(prefix + chalk.cyan.bold('[Not Executed] ' + (tc.summary || tc.url)) + ' (' + trtc.executionTime + 'ms) ');
      }
    });

    Runner.on('error',function(err){
      grunt.log.error();
      over(err);
    });

    Runner.on('log', function(message, level){
      if(!level) level = 'info';
      message = '>> ' + message;
      if(level === 'error') message = chalk.red.bold(message);
      else message = chalk.blue.bold(message);
      console.log(message);
    });

    Runner.on('warning',function(warning){
      //console.log(chalk.yellow('WARNING'));
      //over(warning,true);
    });
  });
};
<commit_msg>failed<commit_after>/*
 * grunt-vrunner
 * http://vrest.io
 *
 * Copyright (c) 2015 vREST Team
 * Licensed under the MIT license.
 */

'use strict';

var vrunner = require('vrunner'), chalk = require('chalk');

module.exports = function(grunt) {

  //add right side padding
  var rpad = function(str, totalSize, padChar){
    if(!padChar) padChar = ' ';
    if(str.length >= totalSize) return str;
    else {
      for(var i = str.length; i < totalSize; i++){
        str += padChar;
      }
      return str;
    }
  };

  var getMethodName = function(method){
    if(method === "GET") return chalk.blue.bold(method) + '    ';
    else if(method === "POST") return chalk.bold(method) + '   ';
    else if(method === "PUT") return chalk.green.bold(method) + '    ';
    else if(method === "PATCH") return chalk.magenta.bold(method) + '  ';
    else if(method === "DELETE") return chalk.red.bold(method) + ' ';
    else return chalk.bold(method) + ' ';
  };

  grunt.registerTask('vrunner', 'Runs vREST test cases.', function() {
    var done = this.async();
    var over = function(err,dontExit){
      if(!err) err = 'An unknown glitch found.';
      if(!Array.isArray(err)) err = [err];
      err.forEach(function(error){
        console.log(error);
      });
      if(!dontExit) done();
      grunt.fail.warn(err);
    };
    var options = this.options();
    var Runner = (new vrunner(options));
    Runner.run(function(err,report,remarks){
      if(err) over(err);
      else {
        //grunt.log.ok('EXECUTION OF ALL TEST CASES SUCCESSFULLY COMPLETED.');
        console.log(remarks,report);
        if(report.failed) over('vRunner test case execution were failed.');
        else done();
      }
    });

    var index = 1;
    Runner.on('testcase',function(pass,tc,trtc){
      //console.log(trtc.);
      var prefix = rpad(index + '.', 5) + getMethodName(tc.method);
      index++;
      if(pass){
        grunt.log.writeln(prefix + chalk.green.bold(tc.summary || tc.url) + ' (' + trtc.executionTime + 'ms) ');
      } else if(pass === false){
        grunt.log.writeln(prefix + chalk.red.bold(tc.summary || tc.url) + ' (' + trtc.executionTime + 'ms) ');
      } else {
        grunt.log.writeln(prefix + chalk.cyan.bold('[Not Executed] ' + (tc.summary || tc.url)) + ' (' + trtc.executionTime + 'ms) ');
      }
    });

    Runner.on('error',function(err){
      grunt.log.error();
      over(err);
    });

    Runner.on('log', function(message, level){
      if(!level) level = 'info';
      message = '>> ' + message;
      if(level === 'error') message = chalk.red.bold(message);
      else message = chalk.blue.bold(message);
      console.log(message);
    });

    Runner.on('warning',function(warning){
      //console.log(chalk.yellow('WARNING'));
      //over(warning,true);
    });
  });
};
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var Game = function(player1,player2) {

  var WIN = 101; // sentinel

  var formatPlayerScore = function(player, score) {
    return player + ' ' + score;
  };

  return {
    _withServes: 0,
    _otherPoints: 0,

    score: function() {
      if (this._withServes === WIN) {
        return player1 + ' won';
      }

      return formatPlayerScore(player1, this._withServes) +
             ' - ' +
             formatPlayerScore(player2, this._otherPoints);
    },

    withServe: function() {
      var pointsProgression = [0,15,30,40,WIN];
      var i = pointsProgression.indexOf(this._withServes);
      this._withServes = pointsProgression[i+1];
    },

    againstServe: function() {
      this._otherPoints += 15;
    }
  };
};

describe('tennis', function() {
  describe('game', function() {
    it('starts at 0-0', function() {
      var game = new Game('Joe', 'Fred');
      assert.equal('Joe 0 - Fred 0', game.score());
    });

    it ('game to love', function() {
      var game = new Game('Joe', 'Fred');

      game.withServe();
      assert.equal('Joe 15 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe 30 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe 40 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe won', game.score());
    });

    it('game against server to love', function() {
      var game = new Game('Joe', 'Fred');

      game.againstServe();
      assert.equal('Joe 0 - Fred 15', game.score());
    });
  });
});
<commit_msg>expected pass<commit_after>"use strict";

var assert = require('assert');

var Game = function(player1,player2) {

  var WIN = 101; // sentinel

  var formatPlayerScore = function(player, score) {
    return player + ' ' + score;
  };

  return {
    _withServes: 0,
    _otherPoints: 0,

    score: function() {
      if (this._withServes === WIN) {
        return player1 + ' won';
      }

      return formatPlayerScore(player1, this._withServes) +
             ' - ' +
             formatPlayerScore(player2, this._otherPoints);
    },

    withServe: function() {
      var pointsProgression = [0,15,30,40,WIN];
      var i = pointsProgression.indexOf(this._withServes);
      this._withServes = pointsProgression[i+1];
    },

    againstServe: function() {
      this._otherPoints += 15;
    }
  };
};

describe('tennis', function() {
  describe('game', function() {
    it('starts at 0-0', function() {
      var game = new Game('Joe', 'Fred');
      assert.equal('Joe 0 - Fred 0', game.score());
    });

    it ('game to love', function() {
      var game = new Game('Joe', 'Fred');

      game.withServe();
      assert.equal('Joe 15 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe 30 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe 40 - Fred 0', game.score());

      game.withServe();
      assert.equal('Joe won', game.score());
    });

    it('game against server to love', function() {
      var game = new Game('Joe', 'Fred');

      game.againstServe();
      assert.equal('Joe 0 - Fred 15', game.score());

      game.againstServe();
      assert.equal('Joe 0 - Fred 30', game.score());
    });
  });
});
<|endoftext|>


<commit_before>import Storage from './Storage/HostStorage';
import ContextualIdentity, {NO_CONTAINER} from './ContextualIdentity';
import Tabs from './Tabs';
import PreferenceStorage from './Storage/PreferenceStorage';

const createTab = (url, newTabIndex, currentTabId, cookieStoreId, openerTabId) => {
  Tabs.get(currentTabId).then((currentTab) => {
    Tabs.create({
      url,
      index: newTabIndex,
      cookieStoreId,
      active: currentTab.active,
      openerTabId: openerTabId,
    });
    PreferenceStorage.get('keepOldTabs').then(({value}) => {
      if(!value){
        Tabs.remove(currentTabId);
      }
    }).catch(() => {
      Tabs.remove(currentTabId);
    });

  });

  return {
    cancel: true,
  };
};

function handle(url, tabId) {
  return Promise.all([
    Storage.get(url),
    ContextualIdentity.getAll(),
    Tabs.get(tabId),
  ]).then(([hostMap, identities, currentTab]) => {

    if (currentTab.incognito || !hostMap) {
      return {};
    }

    const hostIdentity = identities.find((identity) => identity.cookieStoreId === hostMap.cookieStoreId);
    const tabIdentity = identities.find((identity) => identity.cookieStoreId === currentTab.cookieStoreId);

    if (!hostIdentity) {
      return {};
    }

    const openerTabId = currentTab.openerTabId;
    if (hostIdentity.cookieStoreId === NO_CONTAINER.cookieStoreId && tabIdentity) {
      return createTab(requestDetails.url, currentTab.index + 1, currentTab.id, undefined , openerTabId);
    }

    if (hostIdentity.cookieStoreId !== currentTab.cookieStoreId && hostIdentity.cookieStoreId !== NO_CONTAINER.cookieStoreId) {
      return createTab(requestDetails.url, currentTab.index + 1, currentTab.id, hostIdentity.cookieStoreId, openerTabId);
    }

    return {};
  });
}

export const webRequestListener = (requestDetails) => {

  if (requestDetails.frameId !== 0 || requestDetails.tabId === -1) {
    return {};
  }

  return handle(requestDetails.url, requestDetails.tabId);
};

export const tabUpdatedListener = (tabId, changeInfo) => {
  if (!changeInfo.url) {
    return;
  }
  return handle(changeInfo.url, tabId);
};
<commit_msg>Fix post merge errors<commit_after>import Storage from './Storage/HostStorage';
import ContextualIdentity, {NO_CONTAINER} from './ContextualIdentity';
import Tabs from './Tabs';
import PreferenceStorage from './Storage/PreferenceStorage';

const createTab = (url, newTabIndex, currentTabId, cookieStoreId, openerTabId) => {
  Tabs.get(currentTabId).then((currentTab) => {
    Tabs.create({
      url,
      index: newTabIndex,
      cookieStoreId,
      active: currentTab.active,
      openerTabId: openerTabId,
    });
    PreferenceStorage.get('keepOldTabs').then(({value}) => {
      if(!value){
        Tabs.remove(currentTabId);
      }
    }).catch(() => {
      Tabs.remove(currentTabId);
    });

  });

  return {
    cancel: true,
  };
};

function handle(url, tabId) {
  return Promise.all([
    Storage.get(url),
    ContextualIdentity.getAll(),
    Tabs.get(tabId),
  ]).then(([hostMap, identities, currentTab]) => {

    if (currentTab.incognito || !hostMap) {
      return {};
    }

    const hostIdentity = identities.find((identity) => identity.cookieStoreId === hostMap.cookieStoreId);
    const tabIdentity = identities.find((identity) => identity.cookieStoreId === currentTab.cookieStoreId);

    if (!hostIdentity) {
      return {};
    }

    const openerTabId = currentTab.openerTabId;
    if (hostIdentity.cookieStoreId === NO_CONTAINER.cookieStoreId && tabIdentity) {
      return createTab(url, currentTab.index + 1, currentTab.id, undefined , openerTabId);
    }

    if (hostIdentity.cookieStoreId !== currentTab.cookieStoreId && hostIdentity.cookieStoreId !== NO_CONTAINER.cookieStoreId) {
      return createTab(url, currentTab.index + 1, currentTab.id, hostIdentity.cookieStoreId, openerTabId);
    }

    return {};
  });
}

export const webRequestListener = (requestDetails) => {

  if (requestDetails.frameId !== 0 || requestDetails.tabId === -1) {
    return {};
  }

  return handle(requestDetails.url, requestDetails.tabId);
};

export const tabUpdatedListener = (tabId, changeInfo) => {
  if (!changeInfo.url) {
    return;
  }
  return handle(changeInfo.url, tabId);
};
<|endoftext|>


<commit_before>

function controller(httpMethodName, url, body, doneCallback) {

   var
       // the api available on an oboe instance. Will expose 3 methods, onPath, onNode and onError
       events = pubSub(),

       clarinetParser = clarinet.parser(),

       // create a json builder and store a function that can be used to get the
       // root of the json later:
       /**
        * @type {Function}
        */
       objectSoFar = jsonBuilder(
                         clarinetParser,

                         // when a node is found, notify matching node listeners:
                         partialComplete(events.notify, NODE_FOUND_EVENT),

                         // when a node is found, notify matching path listeners:
                         partialComplete(events.notify, PATH_FOUND_EVENT)
                     );

   clarinetParser.onerror =
       function(e) {
          events.notifyErr(e);

          // the json is invalid, give up and close the parser to prevent getting any more:
          clarinetParser.close();
       };

   /* Given a value from the user to send as the request body, return in a form
      that is suitable to sending over the wire. Which is, either a string or
      null.
    */
   function validatedBody( body ) {
      if( !body )
         return null;

      return isString(body)? body: JSON.stringify(body);
   }

   streamingXhr(
      httpMethodName,
      url,
      validatedBody(body),
      function (nextDrip) {
         // callback for when a bit more data arrives from the streaming XHR

         try {
            clarinetParser.write(nextDrip);
         } catch(e) {
            // we don't have to do anything here because we always assign a .onerror
            // to clarinet which will have already been called by the time this
            // exception is thrown.
         }
      },
      function() {
         // callback for when the response is complete
         clarinetParser.close();

         doneCallback && doneCallback(objectSoFar());
      });

   return {
      onPath: partialComplete(events.on, PATH_FOUND_EVENT),

      onNode: partialComplete(events.on, NODE_FOUND_EVENT),

      onError: events.onError,

      root: objectSoFar
   };
}<commit_msg>shortcut method<commit_after>

function controller(httpMethodName, url, body, doneCallback) {

   var
       // the api available on an oboe instance. Will expose 3 methods, onPath, onNode and onError
       events = pubSub(),

       notify = events.notify, // shortcut

       clarinetParser = clarinet.parser(),

       // create a json builder and store a function that can be used to get the
       // root of the json later:
       /**
        * @type {Function}
        */
       objectSoFar = jsonBuilder(
                         clarinetParser,

                         // when a node is found, notify matching node listeners:
                         partialComplete(notify, NODE_FOUND_EVENT),

                         // when a node is found, notify matching path listeners:
                         partialComplete(notify, PATH_FOUND_EVENT)
                     );

   clarinetParser.onerror =
       function(e) {
          events.notifyErr(e);

          // the json is invalid, give up and close the parser to prevent getting any more:
          clarinetParser.close();
       };

   /* Given a value from the user to send as the request body, return in a form
      that is suitable to sending over the wire. Which is, either a string or
      null.
    */
   function validatedBody( body ) {
      if( !body )
         return null;

      return isString(body)? body: JSON.stringify(body);
   }

   streamingXhr(
      httpMethodName,
      url,
      validatedBody(body),
      function (nextDrip) {
         // callback for when a bit more data arrives from the streaming XHR

         try {
            clarinetParser.write(nextDrip);
         } catch(e) {
            // we don't have to do anything here because we always assign a .onerror
            // to clarinet which will have already been called by the time this
            // exception is thrown.
         }
      },
      function() {
         // callback for when the response is complete
         clarinetParser.close();

         doneCallback && doneCallback(objectSoFar());
      });

   return {
      onPath: partialComplete(events.on, PATH_FOUND_EVENT),

      onNode: partialComplete(events.on, NODE_FOUND_EVENT),

      onError: events.onError,

      root: objectSoFar
   };
}<|endoftext|>


<commit_before>/* eslint-disable */
import React, {Component} from 'react';
import hoistStatics from 'hoist-non-react-statics';
import {reaction, autorun} from 'mobx';
import {PropTypes, observer, inject} from 'mobx-react';

const injectorContextTypes = {
  mobxStores: PropTypes.objectOrObservableObject
};
Object.seal(injectorContextTypes);

const proxiedInjectorProps = {
  contextTypes: {
    get() {
      return injectorContextTypes;
    },
    set(_) {
      console.warn('Mobx Injector: you are trying to attach `contextTypes` on an component decorated with `inject` (or `observer`) HOC. Please specify the contextTypes on the wrapped component instead. It is accessible through the `wrappedComponent`');
    },
    configurable: true,
    enumerable: false
  },
  isMobxInjector: {
    value: true,
    writable: true,
    configurable: true,
    enumerable: true
  }
};

/**
 * Store Injection
 */
function createStoreInjector(ctrlName, Ctrl, component, mapper) {
  const displayName = 'inject-' + (component.displayName || component.name || (component.constructor && component.constructor.name) || 'Unknown');

  class Injector extends Component {

    static displayName = displayName;
    controller;
    storeRef = instance => {
      this.wrappedInstance = instance;
    };

    componentWillMount() {
      this.controller = new Ctrl(this.context.mobxStores);
    }

    componentWillUnmount() {
      if(this.controller.$destroy){
        this.controller.$destroy()
      }
      this.controller = null;
    }

    render() {
      // Optimization: it might be more efficient to apply the mapper function *outside* the render method
      // (if the mapper is a function), that could avoid expensive(?) re-rendering of the injector component
      // See this test: 'using a custom injector is not too reactive' in inject.js
      const newProps = {};
      for (const key in this.props) {
        if (this.props.hasOwnProperty(key)) {
          newProps[key] = this.props[key];
        }
      }
      let additionalProps = {};

      if(typeof mapper === 'function'){
        additionalProps = mapper(this.controller, this.props);
      }else{
        additionalProps = (this.context.mobxStores || {}, newProps, this.context) || {};
        additionalProps[ctrlName] = this.controller;
      }

      for (const key in additionalProps) {
        newProps[key] = additionalProps[key];
      }
      newProps.ref = this.storeRef;

      return React.createElement(observer(component), newProps);
    }
  }

  // Static fields from component should be visible on the generated Injector
  hoistStatics(Injector, component);

  Injector.wrappedComponent = component;
  Object.defineProperties(Injector, proxiedInjectorProps);

  return Injector;
}

/**
 * higher order component that injects stores to a child.
 * takes either a varargs list of strings, which are stores read from the context,
 * or a function that manually maps the available stores from the context to props:
 * storesToProps(mobxStores, props, context) => newProps
 */
export default function controller(ctrlName, Ctrl, mapper) {

  function getWrapper(ComponentClass){
    class MyWrapper extends React.Component{
      controller;
      componentWillMount(){
        this.controller = new Ctrl(this.props.stores.stores);
      }
      render(){
        // console.log(this.props.stores)
      const newProps = {};
      for (const key in this.props) {
        if (this.props.hasOwnProperty(key)) {
          newProps[key] = this.props[key];
        }
      }
      newProps[ctrlName] = this.controller;
      return React.createElement(ComponentClass, newProps);

      }
    }
    return (stores)=> {
      return <MyWrapper stores={stores}><ComponentClass/></MyWrapper>
    }
  }

  // return inject((stores) => {
  //   if(typeof mapper === 'function'){
  //     return mapper()
  //   }
  //   return {
  //     [ctrlName]: new Ctrl(stores)
  //   }
  // })
  return function (componentClass) {
    return inject(stores => {
      return {stores}
    })(getWrapper(componentClass))
    // return createStoreInjector(ctrlName, Ctrl, componentClass, mapper);
  };

}
<commit_msg>controller reactor<commit_after>import React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import {inject} from 'mobx-react';
import PropTypes from 'prop-types';

function getHOC(ctrlName, Ctrl, ComponentClass, mapper) {
  class ControllerHOC extends React.Component {
    controller;
    componentWillMount() {
      this.controller = new Ctrl(this.props.stores);
    }
    componentWillUnmount() {
      if (this.controller.$destroy) {
        this.controller.$destroy();
      }
      this.controller = null;
    }
    render() {
      const newProps = {};
      for (const key in this.props) {
        if (Object.prototype.hasOwnProperty.call(this.props, key)) {
          newProps[key] = this.props[key];
        }
      }
      if (typeof mapper === 'function') {
        const additionalProps = mapper(this.controller, newProps);
        for (const key in additionalProps) {
          if (!Object.prototype.hasOwnProperty.call(this.props, key)) {
            newProps[key] = additionalProps[key];
          }
        }
      } else {
        newProps[ctrlName] = this.props[ctrlName] || this.controller;
      }
      return React.createElement(ComponentClass, newProps);
    }
  }
  hoistStatics(ControllerHOC, ComponentClass);
  ControllerHOC.wrappedComponent = ComponentClass;
  ControllerHOC.propTypes = {
    stores: PropTypes.object.isRequired
  };
  return props => {
    return <ControllerHOC {...props}><ComponentClass/></ControllerHOC>;
  };
}

export default function controller(ctrlName, Ctrl, mapper) {
  return function (componentClass) {
    return inject(stores => {
      return {stores};
    })(getHOC(ctrlName, Ctrl, componentClass, mapper));
  };
}
<|endoftext|>


<commit_before>/**
 * https://github.com/ribs
 *
 * Copyright 2014 weber chris
 * Released under the MIT license
 * https://chris.lu
 */

/**
 *
 * ribs controller
 *
 * @param {type} _
 * @param {type} Backbone
 * @returns {_L17.ribs.Controller|ribs.Controller}
 */
define([
    'underscore',
    'backbone'

], function (_, Backbone) {

    'use strict';

    var ribs = {};

    ribs.Controller = function ControllerFunction() {

        this.initialize.apply(this, arguments);

    };

    _.extend(ribs.Controller.prototype, Backbone.Events, {

        initialize: function controllerInitializeFunction(options, configuration, router) {

            this.options = options || {};
            this.router = router;

            // if oninitialize exists
            if (this.onInitialize) {

                // execute it now
                this.onInitialize(this.options, configuration, this.router);

            }

        }
        /*,
        dispatch: function controllerDispatchFunction(containerId) {

            container.dispatch(containerId);

        }*/

    });

    ribs.Controller.extend = Backbone.Model.extend;

    return ribs.Controller;

});
<commit_msg>Update controller.js<commit_after>/**
 * https://github.com/ribs
 *
 * Copyright 2014 weber chris
 * Released under the MIT license
 * https://chris.lu
 */

/**
 *
 * ribs controller
 *
 * @param {type} _
 * @param {type} Backbone
 * @returns {_L17.ribs.Controller|ribs.Controller}
 */
define([
    'underscore',
    'backbone'

], function (_, Backbone) {

    'use strict';

    var ribs = {};

    ribs.Controller = function ControllerFunction() {

        this.initialize.apply(this, arguments);

    };

    _.extend(ribs.Controller.prototype, Backbone.Events, {

        initialize: function controllerInitializeFunction(options, configuration, router) {

            this.options = options || {};
            this.router = router;

            // if oninitialize exists
            if (this.onInitialize) {

                // execute it now
                this.onInitialize(this.options, configuration, this.router);

            }

        }

    });

    ribs.Controller.extend = Backbone.Model.extend;

    return ribs.Controller;

});
<|endoftext|>