Skip to content

Commit

Permalink
Merge pull request #1 from nightshiftjs/setup
Browse files Browse the repository at this point in the history
Setup
  • Loading branch information
thomas-jakemeyn committed Apr 14, 2016
2 parents 73d8507 + 579d719 commit 41e7303
Show file tree
Hide file tree
Showing 11 changed files with 415 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
npm-debug.log
build
.grunt
23 changes: 23 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"esnext": true,
"forin": true,
"globals": {},
"immed": true,
"jasmine": true,
"latedef": "nofunc",
"maxdepth": 2,
"maxstatements": 50,
"newcap": true,
"noarg": true,
"node": true,
"noempty": true,
"nonew": true,
"quotmark": "single",
"strict": true,
"undef": true,
"unused": true
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Change Log
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http.semver.org).

## 0.1.0 - 2016-04-14
### Added
- Created the NightShift core object
146 changes: 146 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
'use strict';

module.exports = function (grunt) {

// project's configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
src: 'src',
tests: 'tests',
build: 'build',
coverage: '<%= build %>/coverage',
coverage_tmp: '<%= coverage %>/tmp',

clean: {
build: {
src: ['<%= build %>']
},
coverage: {
src: ['<%= coverage %>']
},
coverage_tmp: {
src: ['<%= coverage_tmp %>']
}
},

// JSHint's configuration (see http://jshint.com/docs/options/ for the list of available options)
jshint: {
productionCode: {
options: {jshintrc: '.jshintrc'},
src: ['<%= src %>/**/*.js']
},
testCode: {
options: {jshintrc: '.jshintrc'},
src: ['<%= tests %>/**/*.js']
}
},

runTests: {
unit: {
delegate: 'jasmine_nodejs:unit'
},
unitWithCoverage: {
delegate: 'jasmine_nodejs:unitWithCoverage'
}
},

jasmine_nodejs: {
unit: {
specs: ['<%= tests %>/**']
},
unitWithCoverage: {
specs: ['<%= coverage_tmp %>/<%= tests %>/**']
}
},

instrument: {
files: ['<%= src %>/**/*.js'],
options: {
lazy: true,
basePath: '<%= coverage_tmp %>'
}
},

storeCoverage: {
options: {
dir: '<%= coverage_tmp %>'
}
},

makeReport: {
src: '<%= coverage_tmp %>/coverage.json',
options: {
type: 'html',
dir: '<%= coverage %>',
print: 'detail'
}
},

copy: {
toCoverageTmp: {
files: [
{
expand: true,
src: '<%= tests %>/**',
dest: '<%= coverage_tmp %>'
}
]
}
}
});

// plugin for deleting files (see https://github.com/gruntjs/grunt-contrib-clean)
grunt.loadNpmTasks('grunt-contrib-clean');

// plugin for validating files (see https://github.com/gruntjs/grunt-contrib-jshint)
grunt.loadNpmTasks('grunt-contrib-jshint');

// plugin for testing files (see https://github.com/onury/grunt-jasmine-nodejs)
grunt.loadNpmTasks('grunt-jasmine-nodejs');

// plugin for computing test coverage (see https://github.com/taichi/grunt-istanbul)
grunt.loadNpmTasks('grunt-istanbul');

// plugin for copying files (see https://github.com/gruntjs/grunt-contrib-copy)
grunt.loadNpmTasks('grunt-contrib-copy');

// If a file is not required, then it won't be included in the coverage report. Actually, that report does not show
// the coverage of the code but the coverage of the specs, which can be misleading. The workaround is to require the
// instrumented files before running the tests.
grunt.task.registerTask('requireInstrumentedFiles', function () {
var _ = require('lodash');
var glob = require('glob');
_.forEach(glob.sync(grunt.config.get('coverage_tmp') + '/**/!(di.js|*.di.js|*.spec.js).js'), function (file) {
require('./' + file);
});
});

grunt.task.registerMultiTask('runTests', function runTests() {
var data = this.data;

// improve the logging of the errors
require('pretty-error').start();

return grunt.task.run(data.delegate);
});

grunt.task.registerTask('default', ['build']);

grunt.task.registerTask('build', [
'clean:build',
'jshint',
'build-coverage']);

grunt.task.registerTask('build-coverage', [
'clean:coverage',
'instrument',
'requireInstrumentedFiles',
'copy:toCoverageTmp',
'runTests:unitWithCoverage',
'storeCoverage',
'makeReport',
'clean:coverage_tmp'
]);

grunt.task.registerTask('test', ['runTests:unit']);
};
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,86 @@
# nightshift-core
The core of NightShiftJS
NightShift has been designed to be configurable and extendable.

## Configurability
The configurability allows you to use only the features that you need and to avoid undesired dependencies. For example, if you are only interested in [dependency injection](https://github.com/nightshiftjs/nightshift-dependency-injection), then you can configure NightShift as below.

```javascript
var nightShift = require('nightshift-core');
var di = require('nightshift-dependency-injection');

nightShift.plugin(di);
```

NightShift is exactly what you want it to be!

## Extendability
The extendability allows you to enrich NightShift with your own plugins. A NightShift plugin is nothing more than a function that enriches the NightShift core object. For example, the plugin below adds logging capabilities to NightShift.

```javascript
module.exports = function plugin(nightShift) {
nightShift.logger = {...};
};
```

A plugin can rely on the availability of other plugins at runtime. However, it is recommended not to make a plugin explicitly dependent on another plugin, so different versions or implementations of the same plugins can be combined.

## Demo
Discover what you can do with NightShift in this [demo](https://github.com/nightshiftjs/nightshift-demo)!

## Utilities
The NightShift core object provides some utilities.

### functions
#### factoryOf(ConstructorFn)
The usage of the `new` keyword prevents a module from being testable in isolation. Using `new` makes it impossible to test a module without (re-)testing the delegate it instantiates. A solution is to encapsulate the instantiation in a factory which can then be injected in the module and mocked for the testing.

The method `nightShift.functions.factoryOf(ConstructorFn)` creates a factory that can instantiate objects by invoking the given constructor function with the parameters it receives.

```javascript
it('should return a factory for the given constructor function', function () {
var Point = function (x, y) {
this.x = x;
this.y = y;
};
var factory = nightShift.functions.factoryOf(Point);
expect(factory(10, 20)).toEqual(new Point(10, 20));
});
```

### getParamNames(fn)
The method `nightShift.functions.getParamNames(fn)` returns an array listing the names of the parameters of the given function. The array is empty if the function does not expect any parameter.

```javascript
it('should return the names of the parameters of the given function', function () {
expect(nightShift.functions.getParamNames(function () {})).toEqual([]);
expect(nightShift.functions.getParamNames(function (one) {})).toEqual(['one']);
expect(nightShift.functions.getParamNames(function (one, two) {})).toEqual(['one', 'two']);
});
```

### getNbOfParams(fn)
The method `nightShift.functions.getNbOfParams(fn)` returns the number of parameters which are expected by the given function.

```javascript
it('should return the number of parameters of the given function', function () {
expect(nightShift.functions.getNbOfParams(function () {})).toEqual(0);
expect(nightShift.functions.getNbOfParams(function (one) {})).toEqual(1);
expect(nightShift.functions.getNbOfParams(function (one, two) {})).toEqual(2);
});
```

## Tests
The tests can be executed by running the command below.
```
npm install && npm test
```

The test coverage can be checked by running the command below. It executes the tests and it generates a coverage report in _build/coverage/index.html_.
```
npm install && npm build-coverage
```

The quality of the code can be checked by running the command below. It detects potential problems in the code with JSHint, it executes the tests and it generates a coverage report.
```
npm install && npm build
```
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "nightshift-core",
"description": "The core of NightShift",
"version": "0.1.0",
"main": "src/index.js",
"repository": {
"type": "git",
"url": "https://github.com/nightshiftjs/nightshift-core.git"
},
"scripts": {
"build": "grunt build",
"build-coverage": "grunt build-coverage",
"test": "grunt test"
},
"dependencies": {},
"devDependencies": {
"glob": "7.0.3",
"grunt": "1.0.1",
"grunt-contrib-clean": "1.0.0",
"grunt-contrib-copy": "1.0.0",
"grunt-contrib-jshint": "1.0.0",
"grunt-istanbul": "0.7.0",
"grunt-jasmine-nodejs": "1.5.2",
"lodash": "4.9.0",
"pretty-error": "2.0.0"
}
}
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

var functions = require('./utils/functions')();

module.exports = require('./nightshift')(functions);
14 changes: 14 additions & 0 deletions src/nightshift.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

module.exports = function createNightShiftObject(functions) {

function NightShift() {}

NightShift.prototype.functions = functions;

NightShift.prototype.plugin = function(pluginFn) {
return pluginFn(this);
};

return new NightShift();
};
55 changes: 55 additions & 0 deletions src/utils/functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

module.exports = function createFunctionUtils() {

/**
* The usage of the 'new' keyword prevents a module from being testable in isolation. Using 'new' makes it
* impossible to test a module without (re-)testing the delegate it instantiates. A solution is to encapsulate the
* instantiation in a factory which can then be injected in the module and mocked for the testing.
*
* This method creates a factory that can instantiate objects by invoking the given constructor function with the
* parameters it receives.
*
* @param {function} Constructor the constructor function to use for instantiating objects
* @returns {function} a factory function that expects the same parameters as the given constructor function
*/
function factoryOf(Constructor) {
return function () {
var args = Array.prototype.slice.call(arguments);
args.unshift(null);
var BoundConstructor = Function.prototype.bind.apply(Constructor, args);
return new BoundConstructor();
};
}

/**
* This method returns an array listing the names of the parameters of the given function. The array is empty if the
* function does not expect any parameter.
*
* @param {function} fn the function to retrieve the parameters names for
* @returns {Array|{index: number, input: string}} an array listing the names of the parameters of the given
* function, or an empty array if the function does not expect any parameter
*/
function getParamNames(fn) {
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var fnString = fn.toString().replace(STRIP_COMMENTS, '');
var result = fnString.slice(fnString.indexOf('(') + 1, fnString.indexOf(')')).match(/([^\s,]+)/g);
return result || [];
}

/**
* This method returns the number of parameters which are expected by the given function.
*
* @param {function} fn the function to retrieve the number of parameters for
* @returns {Number} the number of parameters which are expected by the given function
*/
function getNbOfParams(fn) {
return getParamNames(fn).length;
}

return {
factoryOf: factoryOf,
getParamNames: getParamNames,
getNbOfParams: getNbOfParams
};
};
Loading

0 comments on commit 41e7303

Please sign in to comment.