emotionLoop/jsCode

View on GitHub
app/models/package.js

Summary

Maintainability
F
6 days
Test Coverage

// # package

var jsonSelect = require( 'mongoose-json-select' );
var mongoosePaginate = require( 'mongoose-paginate' );
var crypto = require( 'crypto' );
var uuid = require( 'uuid' );
var fs = require( 'fs' );
var path = require( 'path' );
var ejs = require( 'ejs' );
var _ = require( 'underscore' );

exports = module.exports = function( mongoose, iglooMongoosePlugin, settings ) {

  var Package = new mongoose.Schema({
    slug: {
      type: String
    },
    settings: {
      type: String,
      required: true
    },
    views: {
      type: Number,
      default: 0
    },
    email: {
      type: String
    }
  });

  // virtuals
  Package.virtual( 'object' ).get( function() {
    return 'package';
  });

  Package.virtual( 'convertedSettings' ).get( function () {
    return convertSettings( this.settings );
  });

  Package.virtual( 'sampleCode' ).get( function () {
    return getSampleCode( this.convertedSettings );
  });

  Package.virtual( 'jshintCode' ).get( function () {
    return getJSHintCode( this.convertedSettings );
  });

  // plugins
  Package.plugin( jsonSelect, '-email' );
  Package.plugin( mongoosePaginate );

  // keep last
  Package.plugin( iglooMongoosePlugin );

  // Generate a unique random slug when saving
  Package.pre( 'save', true, function ( next, done ) {
    var self = this;

    // Only generate a new slug if the package doesn't have one
    if ( this.slug ) {
      next();
      return done();
    }

    var slugSeed = uuid.v4();
    var newSlug = crypto.createHash( 'sha1' ).update( slugSeed ).digest( 'hex' );
    var lastSlugSize = 6;
    var maxSlugSize = 10;
    var finalSlug = newSlug.substr( 0, lastSlugSize );

    // Check if a package with the same slug exists
    function lookForSlugUniqueness() {
      mongoose.models.Package.find({ slug: finalSlug }, checkSlugResults );
    }

    // Check the results of looking for a package with the same slug
    function checkSlugResults( err, matchingPackages ) {

      // No package found, go ahead and return the slug
      if ( ! matchingPackages || matchingPackages.length === 0 ) {
        self.slug = finalSlug;
        return done( null );
      }

      // Otherwise try increasing the size of the slug
      // The slug length should increase only if the first 6 characters correspond to an already existing slug
      ++lastSlugSize;

      // If we have reached the full length of the slug already, generate a new one and try again
      if ( lastSlugSize > maxSlugSize ) {
        newSlug = crypto.createHash( 'sha1' ).update( slugSeed ).digest( 'hex' );
        lastSlugSize = 6;
      }

      finalSlug = newSlug.substr( 0, lastSlugSize );

      lookForSlugUniqueness();
    }

    lookForSlugUniqueness();

    next();
  });

  //
  // Helper methods
  //

  // Get an unsaved package with settings grouped and sorted properly, in JSON format, simulating a package's settings
  Package.methods.simulateSettings = function( stringOfSettingIds ) {
    var arrayOfSettingIds = stringOfSettingIds.split( ',' );

    this.settings = '{}';

    // Parse the ids to Numbers
    arrayOfSettingIds = _.map( arrayOfSettingIds, function( id ) {
      return parseInt( id );
    });

    // Get an array of the settings that were sent in stringOfSettingIds
    var matchingSettings = _.filter( settings, function( setting ) {
      if ( arrayOfSettingIds.indexOf(setting.id) !== -1 ) {
        return true;
      }

      return false;
    });

    // Return nothing if nothing matched
    if ( ! matchingSettings || ! matchingSettings.length ) {
      return this;
    }

    var finalSettings = {};

    // Now we need to "convert" the settings into minimal JSON, like the packages have it
    matchingSettings.forEach( function( setting ) {
      finalSettings[ setting.group ] = setting.name;
    });

    var jsonSettings = JSON.stringify( finalSettings );

    this.settings = jsonSettings;

    return this;
  };

  //
  // Helper functions
  //

  // Convert the package settings from an object into an array with "real" settings
  function convertSettings( settingsJSON ) {
    var chosenSettings = JSON.parse( settingsJSON );
    var finalSettings = [];

    settings.forEach( function( setting ) {
      // If the package has this setting chosen, add it to finalSettings array
      if ( chosenSettings.hasOwnProperty(setting.group) && chosenSettings[setting.group] === setting.name ) {
        finalSettings.push( setting );
      }
    });

    return finalSettings;
  }

  // Get Sample Code from a package settings array
  function getSampleCode( convertedSettings ) {
    // If there are no settings, return an empty string
    if ( convertedSettings.length === 0 ) {
      return '';
    }

    //
    // Define the template options
    //

    var options = {};

    // Options: Indentation
    if ( convertedSettings[0] && convertedSettings[0].name === 'indentation_spaces' ) {
      // Space
      options.space = ' ';
    } else {
      // Tab
      options.space = '\t';
    }

    // Options: Spacing
    if ( convertedSettings[1] && convertedSettings[1].name === 'spacing_2_spaces' ) {
      // 2 Spaces
      if ( options.space === ' ' ) {
        options.indentation = '  ';
      } else {
        options.indentation = '\t';
      }
    } else {
      // 4 Spaces
      if ( options.space === ' ' ) {
        options.indentation = '    ';
      } else {
        options.indentation = '\t';
      }
    }

    // Alias for indentation, so it's not impossible to work on sampleCode.ejs
    options.i = options.indentation;

    // Options: Readability
    if ( convertedSettings[2] && convertedSettings[2].name === 'readability_jumbo' ) {
      // Jumbo
      options.jumboSpace = ' ';
    } else {
      // Normal
      options.jumboSpace = '';
    }

    // Options: Blocks
    if ( convertedSettings[3] && convertedSettings[3].name === 'blocks_inline' ) {
      // Inline
      options.blockSpace = 0;
    } else {
      // Line Break
      options.blockSpace = 1;
    }

    // Options: Variables Naming
    if ( convertedSettings[4] && convertedSettings[4].name === 'variables_naming_descriptive' ) {
      // Descriptive
      options.variableNaming = 0;
    } else {
      // Normal
      options.variableNaming = 1;
    }

    // Options: Variables' Case
    if ( convertedSettings[5] && convertedSettings[5].name === 'variables_case_lower' ) {
      // lower_case
      options.variableCase = 0;
    } else {
      // camelCase
      options.variableCase = 1;
    }

    // Options: Functions Naming
    if ( convertedSettings[6] && convertedSettings[6].name === 'functions_naming_descriptive' ) {
      // Descriptive
      options.functionNaming = 0;
    } else {
      // Normal
      options.functionNaming = 1;
    }

    // Options: Functions Case
    if ( convertedSettings[7] && convertedSettings[7].name === 'functions_case_lower' ) {
      // lower_case
      options.functionCase = 0;
    } else {
      // camelCase
      options.functionCase = 1;
    }

    // Options: Comma in Variable Declaration
    if ( convertedSettings[8] && convertedSettings[8].name === 'variable_declaration_comma_leading' ) {
      // Leading Comma
      options.variableCommaPlace = 0;
    } else {
      // Trailing Comma
      options.variableCommaPlace = 1;
    }

    // Options: Concatenation
    if ( convertedSettings[9] && convertedSettings[9].name === 'variable_concatenation_leading' ) {
      // Leading
      options.concatenationPlace = 0;
    } else {
      // Trailing
      options.concatenationPlace = 1;
    }

    // Options: Quotes
    if ( convertedSettings[10] && convertedSettings[10].name === 'quotes_single' ) {
      // Single
      options.quoteChar = "'";
    } else {
      // Double
      options.quoteChar = '"';
    }

    // Options: Semicolons
    if ( convertedSettings[11] && convertedSettings[11].name === 'semicolons_no' ) {
      // No
      options.semicolon = '';
    } else {
      // Yes
      options.semicolon = ';';
    }

    // Options: Variable Declaration
    if ( convertedSettings[12] && convertedSettings[12].name === 'var_needed' ) {
      // As needed
      options.declareOnce = false;
    } else {
      // once
      options.declareOnce = true;
    }

    // Options: Require Brackets
    if ( convertedSettings[13] && convertedSettings[13].name === 'brackets_needed' ) {
      // As needed
      options.oneLineBrackets = false;
    } else {
      // once
      options.oneLineBrackets = true;
    }

    var templateFile = path.join( __dirname, '../', 'views/sampleCode.ejs' );
    var sampleCodeTemplateContent = fs.readFileSync( templateFile, { encoding: 'utf-8', flag: 'r' });
    var sampleCode = ejs.render( sampleCodeTemplateContent, options );

    return sampleCode;
  }

  // Get jshintrc code from a package settings array
  function getJSHintCode( convertedSettings ) {
    // If there are no settings, return an empty string
    if ( convertedSettings.length === 0 ) {
      return '';
    }

    //
    // Define the template options
    //

    var options = {};

    // Options: Indentation
    // -- Ignored in .jshintrc

    // Options: Spacing
    if ( convertedSettings[1] && convertedSettings[1].name === 'spacing_2_spaces' ) {
      // 2 Spaces
      options.indent = '2';
    } else {
      // 4 Spaces
      options.indent = '4';
    }

    // Options: Readability
    // -- Ignored in .jshintrc

    // Options: Blocks
    if ( convertedSettings[3] && convertedSettings[3].name === 'blocks_inline' ) {
      // Inline
      options.laxBreak = 'false';
    } else {
      // Line Break
      options.laxBreak = 'true';
    }

    // Options: Variables Naming
    // -- Ignored in .jshintrc

    // Options: Variables' Case
    if ( convertedSettings[5] && convertedSettings[5].name === 'variables_case_lower' ) {
      // lower_case
      options.camelCase = 'false';
    } else {
      // camelCase
      options.camelCase = 'true';
    }

    // Options: Functions Naming
    // -- Ignored in .jshintrc

    // Options: Functions Case
    if ( convertedSettings[7] && convertedSettings[7].name === 'functions_case_lower' ) {
      // lower_case
      options.camelCase = 'false';
    } else {
      // camelCase
      options.camelCase = 'true';
    }

    // Options: Comma in Variable Declaration
    if ( convertedSettings[8] && convertedSettings[8].name === 'variable_declaration_comma_leading' ) {
      // Leading Comma
      options.laxComma = 'true';
    } else {
      // Trailing Comma
      options.laxComma = 'false';
    }

    // Options: Concatenation
    // -- Ignored in .jshintrc

    // Options: Quotes
    if ( convertedSettings[10] && convertedSettings[10].name === 'quotes_single' ) {
      // Single
      options.quotMark = 'single';
    } else {
      // Double
      options.quotMark = 'double';
    }

    // Options: Semicolons
    if ( convertedSettings[11] && convertedSettings[11].name === 'semicolons_no' ) {
      // No
      options.asi = 'true';
    } else {
      // Yes
      options.asi = 'false';
    }

    // Options: Variable Declaration
    // -- Ignored in .jshintrc

    // Options: Require Brackets
    if ( convertedSettings[13] && convertedSettings[13].name === 'brackets_needed' ) {
      // As needed
      options.curly = 'false';
    } else {
      // once
      options.curly = 'true';
    }

    var templateFile = path.join( __dirname, '../', 'views/jshintrc.ejs' );
    var jshintrcCodeTemplateContent = fs.readFileSync( templateFile, { encoding: 'utf-8', flag: 'r' });
    var jshintrcCode = ejs.render( jshintrcCodeTemplateContent, options );

    return jshintrcCode;
  }

  return mongoose.model( 'Package', Package );
};

exports['@singleton'] = true;
exports['@require'] = [ 'igloo/mongo', 'igloo/mongoose-plugin', 'models/settings' ];