From 9b715a0732e0d2e6cbd99fea17cb59de76983c76 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 19 Apr 2016 06:39:51 +0200 Subject: [PATCH 001/111] Refactors Space.Logger class to use arguments on Constructor, removes global var Space.log --- source/logger.js | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/source/logger.js b/source/logger.js index bf6488c..9ef664a 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,9 +1,3 @@ -let config = Space.configuration; - -if (Meteor.isServer) { - winston = Npm.require('winston'); -} - Space.Object.extend(Space, 'Logger', { _logger: null, @@ -18,31 +12,12 @@ Space.Object.extend(Space, 'Logger', { 'debug': 7 }, - Constructor() { - if (Meteor.isServer) { - this._logger = new winston.Logger({ - transports: [ - new winston.transports.Console({ - colorize: true, - prettyPrint: true - }) - ] - }); - this._logger.setLevels(winston.config.syslog.levels); - } - if (Meteor.isClient) { - this._logger = console; - } + Constructor(logger) { + this._logger = logger; }, - setMinLevel(name) { - let newCode = this._levelCode(name); - if (this._minLevel !== newCode) { - this._minLevel = newCode; - if (Meteor.isServer) { - this._logger.transports.console.level = name; - } - } + addTransport() { + this._logger.add.apply(this._logger, arguments); }, start() { @@ -95,10 +70,3 @@ Space.Object.extend(Space, 'Logger', { } }); - -Space.log = new Space.Logger(); - -if (config.log.enabled) { - Space.log.setMinLevel(config.log.minLevel); - Space.log.start(); -} From 03aef7d4ea86e1e6940afb54caa4397c282f84da Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 19 Apr 2016 06:41:19 +0200 Subject: [PATCH 002/111] Refactors Space.Module to: not use global Space.log, instantiate underlying library for Space.Logger --- source/module.coffee | 57 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/source/module.coffee b/source/module.coffee index 32719e6..9214fb7 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -22,7 +22,12 @@ class Space.Module extends Space.Object return if not @is('constructed') # only initialize once if not @injector? then throw new Error @ERRORS.injectorMissing @_state = 'configuring' - Space.log.debug("#{@constructor.publishedAs}: initialize") + unless isSubModule + @log = @_setupLogger() + else + @log = @injector.get('log') + @log.debug("#{@constructor.publishedAs}: initialize") + # Setup basic mappings required by all modules if this the top-level module unless isSubModule @injector.map('Injector').to @injector @@ -114,7 +119,7 @@ class Space.Module extends Space.Object # calling the instance hooks before, on, and after _runLifeCycleAction: (action, func) -> @_invokeActionOnRequiredModules action - Space.log.debug("#{@constructor.publishedAs}: #{action}") + @log.debug("#{@constructor.publishedAs}: #{action}") this["before#{Space.capitalizeString(action)}"]?() func?() this["on#{Space.capitalizeString(action)}"]?() @@ -125,7 +130,7 @@ class Space.Module extends Space.Object @_invokeActionOnRequiredModules '_runOnInitializeHooks' # Never run this hook twice if @is('configuring') - Space.log.debug("#{@constructor.publishedAs}: onInitialize") + @log.debug("#{@constructor.publishedAs}: onInitialize") @_state = 'initializing' # Inject required dependencies into this module @injector.injectInto this @@ -135,7 +140,7 @@ class Space.Module extends Space.Object _autoMapSingletons: -> @_invokeActionOnRequiredModules '_autoMapSingletons' if @is('initializing') - Space.log.debug("#{@constructor.publishedAs}: _autoMapSingletons") + @log.debug("#{@constructor.publishedAs}: _autoMapSingletons") @_state = 'auto-mapping-singletons' # Map classes that are declared as singletons @injector.map(singleton).asSingleton() for singleton in @singletons @@ -143,7 +148,7 @@ class Space.Module extends Space.Object _autoCreateSingletons: -> @_invokeActionOnRequiredModules '_autoCreateSingletons' if @is('auto-mapping-singletons') - Space.log.debug("#{@constructor.publishedAs}: _autoCreateSingletons") + @log.debug("#{@constructor.publishedAs}: _autoCreateSingletons") @_state = 'auto-creating-singletons' # Create singleton classes @injector.create(singleton) for singleton in @singletons @@ -153,7 +158,7 @@ class Space.Module extends Space.Object @_invokeActionOnRequiredModules '_runAfterInitializeHooks' # Never run this hook twice if @is('auto-creating-singletons') - Space.log.debug("#{@constructor.publishedAs}: afterInitialize") + @log.debug("#{@constructor.publishedAs}: afterInitialize") @_state = 'initialized' # Call custom lifecycle hook if existant @afterInitialize?() @@ -165,11 +170,47 @@ class Space.Module extends Space.Object this[hook] ?= -> this[hook] = _.wrap(this[hook], wrapper) + _setupLogger: -> + config = Space.configuration.log + + if Meteor.isServer + type = 'winston' + lib = @_setupWinstonLogger(config) + if Meteor.isClient + type = 'console' + lib = console + + logger = new Space.Logger(lib) + @_setupLoggerTransports(type, logger) + logger.start() if config.enabled == true + return logger + + _setupWinstonLogger: (config) -> + winston = Npm.require('winston') + options = + colorize: true + prettyPrint: true + options.level = config.minLevel if config.minLevel? + + lib = new (winston.Logger)(transports: [ + new (winston.transports.Console)(options) + ]) + lib.setLevels winston.config.syslog.levels + return lib + + _setupLoggerTransports: (type, logger) -> + config = @constructor.prototype.configuration + transports = config.log?[type]?.transports + return unless transports + + for transport in transports + logger.addTransport.apply(logger, transport) + _mapSpaceServices: -> - @injector.map('log').to Space.log + @injector.map('log').toStaticValue(@log) _mapMeteorApis: -> - Space.log.debug("#{@constructor.publishedAs}: _mapMeteorApis") + @log.debug("#{@constructor.publishedAs}: _mapMeteorApis") # Map Meteor standard packages @injector.map('Meteor').to Meteor if Package.ejson? From dd32c4ed9154925515d283fc9c2cc327af1b53bb Mon Sep 17 00:00:00 2001 From: qejk Date: Sun, 24 Apr 2016 13:47:33 +0200 Subject: [PATCH 003/111] Moves temporarily Space.Logger code to coffeescript, adds support for multiple logging adapters in Space.Logger, adds adapter for console, winston logging libraries, wires everything up in module.coffee --- package.js | 10 +++- source/logger.coffee | 80 +++++++++++++++++++++++++++ source/logger.js | 72 ------------------------ source/loggers/adapter.js | 3 + source/loggers/console-adapter.coffee | 28 ++++++++++ source/loggers/winston-adapter.coffee | 33 +++++++++++ source/module.coffee | 43 +++++++------- 7 files changed, 176 insertions(+), 93 deletions(-) create mode 100644 source/logger.coffee delete mode 100644 source/logger.js create mode 100644 source/loggers/adapter.js create mode 100644 source/loggers/console-adapter.coffee create mode 100644 source/loggers/winston-adapter.coffee diff --git a/package.js b/package.js index 6ac3c4a..c398c96 100644 --- a/package.js +++ b/package.js @@ -42,15 +42,21 @@ Package.onUse(function(api) { 'source/helpers.coffee', 'source/configuration.js', 'source/object.coffee', - 'source/logger.js', + 'source/logger.coffee', 'source/struct.coffee', 'source/error.js', 'source/injector.coffee', 'source/injector_annotations.coffee', 'source/module.coffee', - 'source/application.coffee' + 'source/application.coffee', + 'source/loggers/adapter.js', + 'source/loggers/console-adapter.coffee', ]); + api.addFiles([ + 'source/loggers/winston-adapter.coffee', + ], 'server'); + // Test helpers api.addFiles([ 'source/testing/bdd-api.coffee' diff --git a/source/logger.coffee b/source/logger.coffee new file mode 100644 index 0000000..6a13732 --- /dev/null +++ b/source/logger.coffee @@ -0,0 +1,80 @@ +Space.Object.extend Space, 'Logger', + + _minLevel: 6 + + _state: 'stopped' + + _levels: + 'error': 3 + 'warning': 4 + 'warn': 4 + 'info': 6 + 'debug': 7 + + Constructor: -> + @_adapters = {} + + addAdapter: (id, adapter, override=false) -> + if not id? + throw new Error(@ERRORS.cannotMapUndefinedId()) + if @existsAdapter(id) and !override + throw new Error(@ERRORS.mappingExists(id)) + check(adapter, Space.Logger.Adapter) + @_adapters[id] = adapter + + overrideAdapter: (id, item) -> + @addAdapter(id, item, true) + + adapter: (id) -> + return @_adapters[id] or null + + existsAdapter: (id) -> + return (@_adapters[id]?) + + removeAdapter: (id) -> + delete @_adapters[id] if @_adapters[id] + + adapters: -> + return @_adapters + + start: -> + if @_is('stopped') + @_state = 'running' + + stop: -> + if @_is('running') + @_state = 'stopped' + + debug: (message) -> + @_log 'debug', arguments + + info: (message) -> + @_log 'info', arguments + + warning: (message) -> + if Meteor.isClient + @_log 'warn', arguments + if Meteor.isServer + @_log 'warning', arguments + + error: (message) -> + @_log 'error', arguments + + _levelCode: (name) -> + @_levels[name] + + _is: (expectedState) -> + if @_state == expectedState + return true + + _log: (level, message) -> + if @_is('running') and @_levelCode(level) <= @_minLevel + for id, adapter of @_adapters + adapter[level].apply adapter, message + + ERRORS: + cannotMapUndefinedId: -> + return "Cannot add adapter with or id" + mappingExists: (id) -> + return "Adapter with id <#{id}> would be overwritten. + Use method for that" \ No newline at end of file diff --git a/source/logger.js b/source/logger.js deleted file mode 100644 index 9ef664a..0000000 --- a/source/logger.js +++ /dev/null @@ -1,72 +0,0 @@ -Space.Object.extend(Space, 'Logger', { - - _logger: null, - _minLevel: 6, - _state: 'stopped', - - _levels: { - 'error': 3, - 'warning': 4, - 'warn': 4, - 'info': 6, - 'debug': 7 - }, - - Constructor(logger) { - this._logger = logger; - }, - - addTransport() { - this._logger.add.apply(this._logger, arguments); - }, - - start() { - if (this._is('stopped')) { - this._state = 'running'; - } - }, - - stop() { - if (this._is('running')) { - this._state = 'stopped'; - } - }, - - debug(message) { - check(message, String); - this._log('debug', arguments); - }, - - info(message) { - check(message, String); - this._log('info', arguments); - }, - - warning(message) { - check(message, String); - if (Meteor.isClient) - this._log('warn', arguments); - if (Meteor.isServer) - this._log('warning', arguments); - }, - - error(message) { - check(message, String); - this._log('error', arguments); - }, - - _levelCode(name) { - return this._levels[name]; - }, - - _is(expectedState) { - if (this._state === expectedState) return true; - }, - - _log(level, message) { - if(this._is('running') && this._levelCode(level) <= this._minLevel) { - this._logger[level].apply(this._logger, message); - } - } - -}); diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js new file mode 100644 index 0000000..c67b531 --- /dev/null +++ b/source/loggers/adapter.js @@ -0,0 +1,3 @@ +Space.Object.extend('Space.Logger.Adapter', {}) + + diff --git a/source/loggers/console-adapter.coffee b/source/loggers/console-adapter.coffee new file mode 100644 index 0000000..8550d7e --- /dev/null +++ b/source/loggers/console-adapter.coffee @@ -0,0 +1,28 @@ +Space.Logger.Adapter.extend 'Space.Logger.ConsoleAdapter', + + _lib: null + + Constructor: (transports=[]) -> + @_lib = console + + addTransport: -> + @_lib.add.apply @_lib, arguments + + debug: (message) -> + check message, String + @_log 'debug', arguments + + info: (message) -> + check message, String + @_log 'info', arguments + + warning: (message) -> + check message, String + @_log 'warn', arguments + + error: (message) -> + check message, String + @_log 'error', arguments + + _log: (level, message) -> + @_lib[level].apply @_lib, message diff --git a/source/loggers/winston-adapter.coffee b/source/loggers/winston-adapter.coffee new file mode 100644 index 0000000..7d4c89d --- /dev/null +++ b/source/loggers/winston-adapter.coffee @@ -0,0 +1,33 @@ +winston = Npm.require('winston') + +Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', + + _lib: null + + Constructor: (transports) -> + @_lib = new winston.Logger({ + transports: transports or [] + }) + @_lib.setLevels(winston.config.syslog.levels) + + addTransport: -> + @_lib.add.apply @_lib, arguments + + debug: (message) -> + check message, String + @_log 'debug', arguments + + info: (message) -> + check message, String + @_log 'info', arguments + + warning: (message) -> + check message, String + @_log 'warning', arguments + + error: (message) -> + check message, String + @_log 'error', arguments + + _log: (level, message) -> + @_lib[level].apply @_lib, message diff --git a/source/module.coffee b/source/module.coffee index 9214fb7..faa3e0b 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -174,37 +174,42 @@ class Space.Module extends Space.Object config = Space.configuration.log if Meteor.isServer + winston = Npm.require('winston') type = 'winston' - lib = @_setupWinstonLogger(config) + adapterClass = Space.Logger.WinstonAdapter + + unless @_hasCustomTransports(type) + defaultTransports = [ + new winston.transports.Console({ + colorize: true, + prettyPrint: true + }) + ] if Meteor.isClient type = 'console' - lib = console + adapterClass = Space.Logger.ConsoleAdapter + defaultTransports = [] - logger = new Space.Logger(lib) - @_setupLoggerTransports(type, logger) - logger.start() if config.enabled == true - return logger + adapter = new adapterClass(defaultTransports) + logger = new Space.Logger() + logger.addAdapter(type, adapter) - _setupWinstonLogger: (config) -> - winston = Npm.require('winston') - options = - colorize: true - prettyPrint: true - options.level = config.minLevel if config.minLevel? + @_setupAdapterTransports(type, adapter) + logger.start() if config.enabled == true - lib = new (winston.Logger)(transports: [ - new (winston.transports.Console)(options) - ]) - lib.setLevels winston.config.syslog.levels - return lib + return logger - _setupLoggerTransports: (type, logger) -> + _setupAdapterTransports: (type, adapter) -> config = @constructor.prototype.configuration transports = config.log?[type]?.transports return unless transports for transport in transports - logger.addTransport.apply(logger, transport) + adapter.addTransport.apply(adapter, transport) + + _hasCustomTransports: (type) -> + config = Space.configuration.log + return (config.log?[type]?.transports?) _mapSpaceServices: -> @injector.map('log').toStaticValue(@log) From 193f43b1ff61f11e90fd77a30e408bf519c8070d Mon Sep 17 00:00:00 2001 From: qejk Date: Sun, 24 Apr 2016 17:12:52 +0200 Subject: [PATCH 004/111] Refactors code: moves methods to base Space.Logger.Addapter class, adds setMinLevel, adds removable transports at winston adapter --- source/logger.coffee | 7 ++++ source/loggers/adapter.js | 49 ++++++++++++++++++++++++++- source/loggers/console-adapter.coffee | 28 +++------------ source/loggers/winston-adapter.coffee | 29 +++++----------- 4 files changed, 67 insertions(+), 46 deletions(-) diff --git a/source/logger.coffee b/source/logger.coffee index 6a13732..1de1b3c 100644 --- a/source/logger.coffee +++ b/source/logger.coffee @@ -60,6 +60,13 @@ Space.Object.extend Space, 'Logger', error: (message) -> @_log 'error', arguments + setMinLevel: (name) -> + newCode = @_levelCode(name) + if @_minLevel != newCode + @_minLevel = newCode + for id, adapter of @_adapters + adapter.setMinLevel(name) + _levelCode: (name) -> @_levels[name] diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index c67b531..992e5f4 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -1,3 +1,50 @@ -Space.Object.extend('Space.Logger.Adapter', {}) +Space.Object.extend('Space.Logger.Adapter', { + + _lib: null, + + debug(message) { + check(message, String); + this._log('debug', arguments); + }, + + info(message) { + check(message, String); + this._log('info', arguments); + }, + + warning(message) { + check(message, String); + this._log('warning', arguments); + }, + + error(message) { + check(message, String); + this._log('error', arguments); + }, + + setMinLevel(name) { + return; + }, + + setLib(lib) { + this._lib = lib; + }, + + lib() { + if (!this._lib) { + throw new Error(this.ERRORS.undefinedLib); + } + return this._lib; + }, + + _log(level, message) { + this._lib[level].apply(this._lib, message); + }, + + ERRORS: { + undefinedLib: 'Logging library is not set on adapter' + } + +}) diff --git a/source/loggers/console-adapter.coffee b/source/loggers/console-adapter.coffee index 8550d7e..1d1d94c 100644 --- a/source/loggers/console-adapter.coffee +++ b/source/loggers/console-adapter.coffee @@ -1,28 +1,8 @@ Space.Logger.Adapter.extend 'Space.Logger.ConsoleAdapter', - _lib: null - - Constructor: (transports=[]) -> - @_lib = console - - addTransport: -> - @_lib.add.apply @_lib, arguments - - debug: (message) -> - check message, String - @_log 'debug', arguments - - info: (message) -> - check message, String - @_log 'info', arguments + Constructor: () -> + @setLib(console) warning: (message) -> - check message, String - @_log 'warn', arguments - - error: (message) -> - check message, String - @_log 'error', arguments - - _log: (level, message) -> - @_lib[level].apply @_lib, message + check(message, String) + @_log('warn', arguments) diff --git a/source/loggers/winston-adapter.coffee b/source/loggers/winston-adapter.coffee index 7d4c89d..ea46965 100644 --- a/source/loggers/winston-adapter.coffee +++ b/source/loggers/winston-adapter.coffee @@ -2,32 +2,19 @@ winston = Npm.require('winston') Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', - _lib: null - Constructor: (transports) -> - @_lib = new winston.Logger({ + lib = new winston.Logger({ transports: transports or [] }) - @_lib.setLevels(winston.config.syslog.levels) + lib.setLevels(winston.config.syslog.levels) + @setLib(lib) addTransport: -> @_lib.add.apply @_lib, arguments - debug: (message) -> - check message, String - @_log 'debug', arguments - - info: (message) -> - check message, String - @_log 'info', arguments - - warning: (message) -> - check message, String - @_log 'warning', arguments - - error: (message) -> - check message, String - @_log 'error', arguments + removeTransport: -> + @_lib.remove.apply @_lib, arguments - _log: (level, message) -> - @_lib[level].apply @_lib, message + setMinLevel: (name) -> + for id, transports of @_lib.transports + @_lib.transports[id].level = name \ No newline at end of file From c71872c601996a6786de3653473b82e4c984eba8 Mon Sep 17 00:00:00 2001 From: qejk Date: Sun, 24 Apr 2016 17:14:14 +0200 Subject: [PATCH 005/111] Adds default configuration from ENV to module configuration, sets minLevel on logger --- source/module.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/module.coffee b/source/module.coffee index faa3e0b..bdb7030 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -21,6 +21,7 @@ class Space.Module extends Space.Object initialize: (@app, @injector, isSubModule=false) -> return if not @is('constructed') # only initialize once if not @injector? then throw new Error @ERRORS.injectorMissing + @_setDefaultsToConfiguration(@configuration) @_state = 'configuring' unless isSubModule @log = @_setupLogger() @@ -115,6 +116,14 @@ class Space.Module extends Space.Object else return module + _setDefaultsToConfiguration: (configuration) -> + _.defaults(configuration, Space.getenv.multi({ + log: { + enabled: ['SPACE_LOG_ENABLED', false, 'bool'], + minLevel: ['SPACE_LOG_MIN_LEVEL', 'info', 'string'] + } + })) + # Invokes the lifecycle action on all required modules, then on itself, # calling the instance hooks before, on, and after _runLifeCycleAction: (action, func) -> @@ -193,6 +202,7 @@ class Space.Module extends Space.Object adapter = new adapterClass(defaultTransports) logger = new Space.Logger() logger.addAdapter(type, adapter) + logger.setMinLevel(config.minLevel) if config.minLevel? @_setupAdapterTransports(type, adapter) logger.start() if config.enabled == true From a778812b42e296a9b2d6ef711d19f79e77ed3c7d Mon Sep 17 00:00:00 2001 From: qejk Date: Sun, 24 Apr 2016 18:22:09 +0200 Subject: [PATCH 006/111] Refactors --- source/configuration.js | 33 ---------------- source/logger.coffee | 26 ++---------- source/loggers/adapter.js | 4 -- source/loggers/winston-adapter.coffee | 14 +++++-- source/module.coffee | 57 +++++++++++++++------------ 5 files changed, 47 insertions(+), 87 deletions(-) diff --git a/source/configuration.js b/source/configuration.js index 3c7095b..bd67ee2 100644 --- a/source/configuration.js +++ b/source/configuration.js @@ -4,37 +4,4 @@ if (Meteor.isServer) { let getenv = Npm.require('getenv'); // Wrapper Space.getenv = getenv; - - Space.configuration = Space.getenv.multi({ - log: { - enabled: ['SPACE_LOG_ENABLED', false, 'bool'], - minLevel: ['SPACE_LOG_MIN_LEVEL', 'info', 'string'] - } - }); - - // Pass down to the client - _.deepExtend(Meteor.settings, { - public: { - log: { - enabled: Space.configuration.log.enabled, - minLevel: Space.configuration.log.minLevel - } - } - }); - - __meteor_runtime_config__.PUBLIC_SETTINGS = Meteor.settings.public; - -} - -if (Meteor.isClient) { - - let log = Meteor.settings.public.log; - - // Guard and defaults when not loaded on server - Space.configuration = { - log: { - enabled: log && log.enabled || false, - minLevel: log && log.minLevel || 'info' - } - }; } diff --git a/source/logger.coffee b/source/logger.coffee index 1de1b3c..4993195 100644 --- a/source/logger.coffee +++ b/source/logger.coffee @@ -1,16 +1,7 @@ Space.Object.extend Space, 'Logger', - _minLevel: 6 - _state: 'stopped' - _levels: - 'error': 3 - 'warning': 4 - 'warn': 4 - 'info': 6 - 'debug': 7 - Constructor: -> @_adapters = {} @@ -60,24 +51,15 @@ Space.Object.extend Space, 'Logger', error: (message) -> @_log 'error', arguments - setMinLevel: (name) -> - newCode = @_levelCode(name) - if @_minLevel != newCode - @_minLevel = newCode - for id, adapter of @_adapters - adapter.setMinLevel(name) - - _levelCode: (name) -> - @_levels[name] - _is: (expectedState) -> if @_state == expectedState return true _log: (level, message) -> - if @_is('running') and @_levelCode(level) <= @_minLevel - for id, adapter of @_adapters - adapter[level].apply adapter, message + return unless @_is('running') + + for id, adapter of @_adapters + adapter[level].apply adapter, message ERRORS: cannotMapUndefinedId: -> diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index 992e5f4..4ac1377 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -22,10 +22,6 @@ Space.Object.extend('Space.Logger.Adapter', { this._log('error', arguments); }, - setMinLevel(name) { - return; - }, - setLib(lib) { this._lib = lib; }, diff --git a/source/loggers/winston-adapter.coffee b/source/loggers/winston-adapter.coffee index ea46965..62bc5b7 100644 --- a/source/loggers/winston-adapter.coffee +++ b/source/loggers/winston-adapter.coffee @@ -15,6 +15,14 @@ Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', removeTransport: -> @_lib.remove.apply @_lib, arguments - setMinLevel: (name) -> - for id, transports of @_lib.transports - @_lib.transports[id].level = name \ No newline at end of file + hasTransport: (name) -> + return @_lib.transports[transportName]? + + setMinLevel: (transportName, levelName) -> + unless @hasTransport(transportName) + throw new Error(@ERRORS.transportNotAdded(transportName)) + @_lib.transports[transportName].level = levelName + + ERRORS: + transportNotAdded: (transportName) -> + return "Winston transport with #{transportName} is not added" \ No newline at end of file diff --git a/source/module.coffee b/source/module.coffee index bdb7030..48aad55 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -21,7 +21,7 @@ class Space.Module extends Space.Object initialize: (@app, @injector, isSubModule=false) -> return if not @is('constructed') # only initialize once if not @injector? then throw new Error @ERRORS.injectorMissing - @_setDefaultsToConfiguration(@configuration) + @_setDefaultsOnConfiguration(@configuration) @_state = 'configuring' unless isSubModule @log = @_setupLogger() @@ -116,11 +116,14 @@ class Space.Module extends Space.Object else return module - _setDefaultsToConfiguration: (configuration) -> - _.defaults(configuration, Space.getenv.multi({ + _setDefaultsOnConfiguration: (configuration) -> + _.extend(configuration, Space.getenv.multi({ log: { enabled: ['SPACE_LOG_ENABLED', false, 'bool'], - minLevel: ['SPACE_LOG_MIN_LEVEL', 'info', 'string'] + console: { + enabled: ['SPACE_CONSOLE_LOG_ENABLED', false, 'bool'], + minLevel: ['SPACE_CONSOLE_LOG_MIN_LEVEL', 'info', 'string'] + } } })) @@ -180,46 +183,50 @@ class Space.Module extends Space.Object this[hook] = _.wrap(this[hook], wrapper) _setupLogger: -> - config = Space.configuration.log + config = @constructor.prototype.configuration.log or {} + defaultTransports = [] if Meteor.isServer - winston = Npm.require('winston') - type = 'winston' + adapterName = 'winston' adapterClass = Space.Logger.WinstonAdapter + unless @_areCustomAdapterTransportsConfigured(adapterName, config) + defaultTransports = @_getDefaultTransportsForWinston(config) - unless @_hasCustomTransports(type) - defaultTransports = [ - new winston.transports.Console({ - colorize: true, - prettyPrint: true - }) - ] if Meteor.isClient - type = 'console' + adapterName = 'console' adapterClass = Space.Logger.ConsoleAdapter - defaultTransports = [] adapter = new adapterClass(defaultTransports) logger = new Space.Logger() - logger.addAdapter(type, adapter) - logger.setMinLevel(config.minLevel) if config.minLevel? + logger.addAdapter(adapterName, adapter) - @_setupAdapterTransports(type, adapter) + @_setupAdapterTransports(adapterName, adapter, config) logger.start() if config.enabled == true return logger - _setupAdapterTransports: (type, adapter) -> - config = @constructor.prototype.configuration - transports = config.log?[type]?.transports + _setupAdapterTransports: (adapterName, adapter, config) -> + transports = config[adapterName]?.transports return unless transports for transport in transports adapter.addTransport.apply(adapter, transport) - _hasCustomTransports: (type) -> - config = Space.configuration.log - return (config.log?[type]?.transports?) + _getDefaultTransportsForWinston: (config) -> + winston = Npm.require('winston') + defaultTransports = [] + + if config.console?.enabled + options = + colorize: true + prettyPrint: true + options.level = config.console.minLevel if config.console?.minLevel? + defaultTransports.push(new winston.transports.Console(options)) + + return defaultTransports + + _areCustomAdapterTransportsConfigured: (adapterName, config) -> + return (config[adapterName]?.transports?) _mapSpaceServices: -> @injector.map('log').toStaticValue(@log) From c8ea9ea547673c5b4b597aaf7094507250b87950 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 09:48:34 +0200 Subject: [PATCH 007/111] Removes blank lines --- source/configuration.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/configuration.js b/source/configuration.js index bd67ee2..29f1c53 100644 --- a/source/configuration.js +++ b/source/configuration.js @@ -1,6 +1,4 @@ - if (Meteor.isServer) { - let getenv = Npm.require('getenv'); // Wrapper Space.getenv = getenv; From 89420548febc9ca7f73789d7fc295485b0576835 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 09:53:50 +0200 Subject: [PATCH 008/111] Moves instantiation of console transport to Space.Logger.WinstonAdapter --- source/loggers/winston-adapter.coffee | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source/loggers/winston-adapter.coffee b/source/loggers/winston-adapter.coffee index 62bc5b7..b7d1d6c 100644 --- a/source/loggers/winston-adapter.coffee +++ b/source/loggers/winston-adapter.coffee @@ -1,6 +1,6 @@ winston = Npm.require('winston') -Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', +Space.Logger.WinstonAdapter = Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', Constructor: (transports) -> lib = new winston.Logger({ @@ -25,4 +25,14 @@ Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', ERRORS: transportNotAdded: (transportName) -> - return "Winston transport with #{transportName} is not added" \ No newline at end of file + return "Winston transport with #{transportName} is not added" + +Space.Logger.WinstonAdapter.console = (options) -> + unless options? + options = + colorize: true + prettyPrint: true + level: 'info' + + return new winston.transports.Console(options) + From 64f90e0bff5f83fff04f841aa024b4ae75fe902b Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 09:54:43 +0200 Subject: [PATCH 009/111] Simplifies initialization of transports, adds dedicated method for preparing logging configuration --- source/module.coffee | 76 +++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/source/module.coffee b/source/module.coffee index 48aad55..eb6392f 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -21,7 +21,7 @@ class Space.Module extends Space.Object initialize: (@app, @injector, isSubModule=false) -> return if not @is('constructed') # only initialize once if not @injector? then throw new Error @ERRORS.injectorMissing - @_setDefaultsOnConfiguration(@configuration) + @_state = 'configuring' unless isSubModule @log = @_setupLogger() @@ -116,17 +116,6 @@ class Space.Module extends Space.Object else return module - _setDefaultsOnConfiguration: (configuration) -> - _.extend(configuration, Space.getenv.multi({ - log: { - enabled: ['SPACE_LOG_ENABLED', false, 'bool'], - console: { - enabled: ['SPACE_CONSOLE_LOG_ENABLED', false, 'bool'], - minLevel: ['SPACE_CONSOLE_LOG_MIN_LEVEL', 'info', 'string'] - } - } - })) - # Invokes the lifecycle action on all required modules, then on itself, # calling the instance hooks before, on, and after _runLifeCycleAction: (action, func) -> @@ -183,50 +172,63 @@ class Space.Module extends Space.Object this[hook] = _.wrap(this[hook], wrapper) _setupLogger: -> - config = @constructor.prototype.configuration.log or {} - defaultTransports = [] + if Meteor.isServer + config = @_loggingConfig(@configuration) + else + config = {} + transports = [] if Meteor.isServer adapterName = 'winston' adapterClass = Space.Logger.WinstonAdapter - unless @_areCustomAdapterTransportsConfigured(adapterName, config) - defaultTransports = @_getDefaultTransportsForWinston(config) + transports = config.winston?.transports or [] if Meteor.isClient adapterName = 'console' adapterClass = Space.Logger.ConsoleAdapter - adapter = new adapterClass(defaultTransports) + adapter = new adapterClass(transports) logger = new Space.Logger() logger.addAdapter(adapterName, adapter) - - @_setupAdapterTransports(adapterName, adapter, config) logger.start() if config.enabled == true return logger - _setupAdapterTransports: (adapterName, adapter, config) -> - transports = config[adapterName]?.transports - return unless transports - - for transport in transports - adapter.addTransport.apply(adapter, transport) + _loggingConfig: () -> + config = {} + _.deepExtend(config, Space.getenv.multi({ + log: { + enabled: ['SPACE_LOG_ENABLED', false, 'bool'], + winston: { + console: { + enabled: ['SPACE_CONSOLE_LOG_ENABLED', false, 'bool'], + minLevel: ['SPACE_CONSOLE_LOG_MIN_LEVEL', 'info', 'string'] + } + } + } + })) + _.deepExtend(config, @configuration) + _.deepExtend(config, @constructor.prototype.configuration) - _getDefaultTransportsForWinston: (config) -> - winston = Npm.require('winston') - defaultTransports = [] + winstonCfg = config.log?.winston or {} - if config.console?.enabled - options = - colorize: true - prettyPrint: true - options.level = config.console.minLevel if config.console?.minLevel? - defaultTransports.push(new winston.transports.Console(options)) + if _.isEmpty(winstonCfg.transports) and winstonCfg.console.enabled == true + _.deepExtend(config, log: { + winston: { + transports: [@_setupWinstonConsoleTransport( + winstonCfg.console.minLevel + )] + } + }) - return defaultTransports + return config.log or {} - _areCustomAdapterTransportsConfigured: (adapterName, config) -> - return (config[adapterName]?.transports?) + _setupWinstonConsoleTransport: (minLevel) -> + options = + colorize: true + prettyPrint: true + options.level = minLevel if minLevel? + return Space.Logger.WinstonAdapter.console(options) _mapSpaceServices: -> @injector.map('log').toStaticValue(@log) From 462bfef3be3647bbf368c77eca0aac4230c102be Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 21:17:08 +0200 Subject: [PATCH 010/111] Removes winston npm dependency --- .npm/package/npm-shrinkwrap.json | 26 ------------------ package.js | 7 +---- source/loggers/winston-adapter.coffee | 38 --------------------------- 3 files changed, 1 insertion(+), 70 deletions(-) delete mode 100644 source/loggers/winston-adapter.coffee diff --git a/.npm/package/npm-shrinkwrap.json b/.npm/package/npm-shrinkwrap.json index 21e3e9c..58c8671 100644 --- a/.npm/package/npm-shrinkwrap.json +++ b/.npm/package/npm-shrinkwrap.json @@ -2,32 +2,6 @@ "dependencies": { "getenv": { "version": "0.5.0" - }, - "winston": { - "version": "2.1.0", - "dependencies": { - "async": { - "version": "1.0.0" - }, - "colors": { - "version": "1.0.3" - }, - "cycle": { - "version": "1.0.3" - }, - "eyes": { - "version": "0.1.8" - }, - "isstream": { - "version": "0.1.2" - }, - "pkginfo": { - "version": "0.3.1" - }, - "stack-trace": { - "version": "0.0.9" - } - } } } } diff --git a/package.js b/package.js index c398c96..b93eadb 100644 --- a/package.js +++ b/package.js @@ -7,8 +7,7 @@ Package.describe({ }); Npm.depends({ - "getenv": "0.5.0", - "winston": "2.1.0" + "getenv": "0.5.0" }); Package.onUse(function(api) { @@ -53,10 +52,6 @@ Package.onUse(function(api) { 'source/loggers/console-adapter.coffee', ]); - api.addFiles([ - 'source/loggers/winston-adapter.coffee', - ], 'server'); - // Test helpers api.addFiles([ 'source/testing/bdd-api.coffee' diff --git a/source/loggers/winston-adapter.coffee b/source/loggers/winston-adapter.coffee deleted file mode 100644 index b7d1d6c..0000000 --- a/source/loggers/winston-adapter.coffee +++ /dev/null @@ -1,38 +0,0 @@ -winston = Npm.require('winston') - -Space.Logger.WinstonAdapter = Space.Logger.Adapter.extend 'Space.Logger.WinstonAdapter', - - Constructor: (transports) -> - lib = new winston.Logger({ - transports: transports or [] - }) - lib.setLevels(winston.config.syslog.levels) - @setLib(lib) - - addTransport: -> - @_lib.add.apply @_lib, arguments - - removeTransport: -> - @_lib.remove.apply @_lib, arguments - - hasTransport: (name) -> - return @_lib.transports[transportName]? - - setMinLevel: (transportName, levelName) -> - unless @hasTransport(transportName) - throw new Error(@ERRORS.transportNotAdded(transportName)) - @_lib.transports[transportName].level = levelName - - ERRORS: - transportNotAdded: (transportName) -> - return "Winston transport with #{transportName} is not added" - -Space.Logger.WinstonAdapter.console = (options) -> - unless options? - options = - colorize: true - prettyPrint: true - level: 'info' - - return new winston.transports.Console(options) - From dc4914f2e7e822b939798176e6a9e69a80a6ab93 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 21:18:04 +0200 Subject: [PATCH 011/111] Simplifies logging initialization by moving it to separate modules contained in additional packages --- source/module.coffee | 49 +------------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/source/module.coffee b/source/module.coffee index eb6392f..8be9175 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -172,64 +172,17 @@ class Space.Module extends Space.Object this[hook] = _.wrap(this[hook], wrapper) _setupLogger: -> - if Meteor.isServer - config = @_loggingConfig(@configuration) - else - config = {} - - transports = [] - if Meteor.isServer - adapterName = 'winston' - adapterClass = Space.Logger.WinstonAdapter - transports = config.winston?.transports or [] - - if Meteor.isClient - adapterName = 'console' - adapterClass = Space.Logger.ConsoleAdapter - - adapter = new adapterClass(transports) + config = @_loggingConfig(@configuration) logger = new Space.Logger() - logger.addAdapter(adapterName, adapter) logger.start() if config.enabled == true - return logger _loggingConfig: () -> config = {} - _.deepExtend(config, Space.getenv.multi({ - log: { - enabled: ['SPACE_LOG_ENABLED', false, 'bool'], - winston: { - console: { - enabled: ['SPACE_CONSOLE_LOG_ENABLED', false, 'bool'], - minLevel: ['SPACE_CONSOLE_LOG_MIN_LEVEL', 'info', 'string'] - } - } - } - })) _.deepExtend(config, @configuration) _.deepExtend(config, @constructor.prototype.configuration) - - winstonCfg = config.log?.winston or {} - - if _.isEmpty(winstonCfg.transports) and winstonCfg.console.enabled == true - _.deepExtend(config, log: { - winston: { - transports: [@_setupWinstonConsoleTransport( - winstonCfg.console.minLevel - )] - } - }) - return config.log or {} - _setupWinstonConsoleTransport: (minLevel) -> - options = - colorize: true - prettyPrint: true - options.level = minLevel if minLevel? - return Space.Logger.WinstonAdapter.console(options) - _mapSpaceServices: -> @injector.map('log').toStaticValue(@log) From a48baa34719d947b7226498b8aea6fcaa1cfeaca Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 26 Apr 2016 21:39:10 +0200 Subject: [PATCH 012/111] Moves coffeescript to ES6 /cry --- package.js | 4 +- source/logger.coffee | 69 ----------------- source/logger.js | 102 ++++++++++++++++++++++++++ source/loggers/adapter.js | 4 +- source/loggers/console-adapter.coffee | 8 -- source/loggers/console-adapter.js | 12 +++ 6 files changed, 117 insertions(+), 82 deletions(-) delete mode 100644 source/logger.coffee create mode 100644 source/logger.js delete mode 100644 source/loggers/console-adapter.coffee create mode 100644 source/loggers/console-adapter.js diff --git a/package.js b/package.js index b93eadb..e7d1340 100644 --- a/package.js +++ b/package.js @@ -41,7 +41,7 @@ Package.onUse(function(api) { 'source/helpers.coffee', 'source/configuration.js', 'source/object.coffee', - 'source/logger.coffee', + 'source/logger.js', 'source/struct.coffee', 'source/error.js', 'source/injector.coffee', @@ -49,7 +49,7 @@ Package.onUse(function(api) { 'source/module.coffee', 'source/application.coffee', 'source/loggers/adapter.js', - 'source/loggers/console-adapter.coffee', + 'source/loggers/console-adapter.js', ]); // Test helpers diff --git a/source/logger.coffee b/source/logger.coffee deleted file mode 100644 index 4993195..0000000 --- a/source/logger.coffee +++ /dev/null @@ -1,69 +0,0 @@ -Space.Object.extend Space, 'Logger', - - _state: 'stopped' - - Constructor: -> - @_adapters = {} - - addAdapter: (id, adapter, override=false) -> - if not id? - throw new Error(@ERRORS.cannotMapUndefinedId()) - if @existsAdapter(id) and !override - throw new Error(@ERRORS.mappingExists(id)) - check(adapter, Space.Logger.Adapter) - @_adapters[id] = adapter - - overrideAdapter: (id, item) -> - @addAdapter(id, item, true) - - adapter: (id) -> - return @_adapters[id] or null - - existsAdapter: (id) -> - return (@_adapters[id]?) - - removeAdapter: (id) -> - delete @_adapters[id] if @_adapters[id] - - adapters: -> - return @_adapters - - start: -> - if @_is('stopped') - @_state = 'running' - - stop: -> - if @_is('running') - @_state = 'stopped' - - debug: (message) -> - @_log 'debug', arguments - - info: (message) -> - @_log 'info', arguments - - warning: (message) -> - if Meteor.isClient - @_log 'warn', arguments - if Meteor.isServer - @_log 'warning', arguments - - error: (message) -> - @_log 'error', arguments - - _is: (expectedState) -> - if @_state == expectedState - return true - - _log: (level, message) -> - return unless @_is('running') - - for id, adapter of @_adapters - adapter[level].apply adapter, message - - ERRORS: - cannotMapUndefinedId: -> - return "Cannot add adapter with or id" - mappingExists: (id) -> - return "Adapter with id <#{id}> would be overwritten. - Use method for that" \ No newline at end of file diff --git a/source/logger.js b/source/logger.js new file mode 100644 index 0000000..3c71640 --- /dev/null +++ b/source/logger.js @@ -0,0 +1,102 @@ +Space.Object.extend('Space.Logger', { + + _state: 'stopped', + + Constructor() { + return this._adapters = {}; + }, + + addAdapter(id, adapter, override) { + if (override == null) { + let override = false; + } + if (id == null) { + throw new Error(this.ERRORS.cannotMapUndefinedId()); + } + if (this.existsAdapter(id) && !override) { + throw new Error(this.ERRORS.mappingExists(id)); + } + check(adapter, Space.Logger.Adapter); + return this._adapters[id] = adapter; + }, + + overrideAdapter(id, item) { + return this.addAdapter(id, item, true); + }, + + adapter(id) { + return this._adapters[id] || null; + }, + + existsAdapter(id) { + return (this._adapters[id] != null); + }, + + removeAdapter(id) { + if (this._adapters[id]) { + return delete this._adapters[id]; + } + }, + + adapters() { + return this._adapters; + }, + + start() { + if (this._is('stopped')) { + return this._state = 'running'; + } + }, + + stop() { + if (this._is('running')) { + return this._state = 'stopped'; + } + }, + + debug(message) { + return this._log('debug', arguments); + }, + + info(message) { + return this._log('info', arguments); + }, + + warning(message) { + if (Meteor.isClient) { + this._log('warn', arguments); + } + if (Meteor.isServer) { + return this._log('warning', arguments); + } + }, + + error(message) { + return this._log('error', arguments); + }, + + _is(expectedState) { + if (this._state === expectedState) { + return true; + } + }, + + _log(level, message) { + if (!this._is('running')) { + return; + } + + _.each(this._adapters, function(adapter, id) { + adapter[level].apply(adapter, message); + }); + }, + + ERRORS: { + cannotMapUndefinedId() { + return "Cannot add adapter with or id"; + }, + mappingExists(id) { + return `Adapter with id <${id}> would be overwritten. Use method for that`; + } + } +}); diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index 4ac1377..e6e6f66 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -41,6 +41,4 @@ Space.Object.extend('Space.Logger.Adapter', { undefinedLib: 'Logging library is not set on adapter' } -}) - - +}); \ No newline at end of file diff --git a/source/loggers/console-adapter.coffee b/source/loggers/console-adapter.coffee deleted file mode 100644 index 1d1d94c..0000000 --- a/source/loggers/console-adapter.coffee +++ /dev/null @@ -1,8 +0,0 @@ -Space.Logger.Adapter.extend 'Space.Logger.ConsoleAdapter', - - Constructor: () -> - @setLib(console) - - warning: (message) -> - check(message, String) - @_log('warn', arguments) diff --git a/source/loggers/console-adapter.js b/source/loggers/console-adapter.js new file mode 100644 index 0000000..5b86444 --- /dev/null +++ b/source/loggers/console-adapter.js @@ -0,0 +1,12 @@ +Space.Logger.Adapter.extend('Space.Logger.ConsoleAdapter', { + + Constructor() { + return this.setLib(console); + }, + + warning(message) { + check(message, String); + return this._log('warn', arguments); + } + +}); \ No newline at end of file From 0cd08c66d2bc32a47baeaf3f3c508d75aa093114 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:01:53 +0200 Subject: [PATCH 013/111] Removes unnecessary variable --- source/configuration.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/configuration.js b/source/configuration.js index 29f1c53..35ec25a 100644 --- a/source/configuration.js +++ b/source/configuration.js @@ -1,5 +1,3 @@ if (Meteor.isServer) { - let getenv = Npm.require('getenv'); - // Wrapper - Space.getenv = getenv; + Space.getenv = Npm.require('getenv'); } From e5a071acb6d0244b424c9d44870547ee6c12fd1e Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:02:37 +0200 Subject: [PATCH 014/111] Adds state related additional helper methods to Space.Logger --- source/logger.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/source/logger.js b/source/logger.js index 3c71640..be47339 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,8 +1,12 @@ Space.Object.extend('Space.Logger', { - _state: 'stopped', + STATES: { + stopped: 'stopped', + running: 'running' + }, Constructor() { + this._state = this.STATES.stopped; return this._adapters = {}; }, @@ -10,9 +14,7 @@ Space.Object.extend('Space.Logger', { if (override == null) { let override = false; } - if (id == null) { - throw new Error(this.ERRORS.cannotMapUndefinedId()); - } + check(id, String); if (this.existsAdapter(id) && !override) { throw new Error(this.ERRORS.mappingExists(id)); } @@ -43,14 +45,14 @@ Space.Object.extend('Space.Logger', { }, start() { - if (this._is('stopped')) { - return this._state = 'running'; + if (this._is(this.STATES.stopped)) { + return this._state = this.STATES.running; } }, stop() { - if (this._is('running')) { - return this._state = 'stopped'; + if (this._is(this.STATES.running)) { + return this._state = this.STATES.stopped;; } }, @@ -63,12 +65,7 @@ Space.Object.extend('Space.Logger', { }, warning(message) { - if (Meteor.isClient) { - this._log('warn', arguments); - } - if (Meteor.isServer) { - return this._log('warning', arguments); - } + return this._log('warning', arguments); }, error(message) { @@ -76,13 +73,19 @@ Space.Object.extend('Space.Logger', { }, _is(expectedState) { - if (this._state === expectedState) { - return true; - } + return (this._state === expectedState); + }, + + isRunning() { + return this._is(this.STATES.running); + }, + + isStopped() { + return this._is(this.STATES.stopped); }, _log(level, message) { - if (!this._is('running')) { + if (!this._is(this.STATES.running)) { return; } @@ -92,9 +95,6 @@ Space.Object.extend('Space.Logger', { }, ERRORS: { - cannotMapUndefinedId() { - return "Cannot add adapter with or id"; - }, mappingExists(id) { return `Adapter with id <${id}> would be overwritten. Use method for that`; } From e06d2644d87049064f9bf7956ff76e6296121d9d Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:03:04 +0200 Subject: [PATCH 015/111] Changes injector mapping type --- source/module.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/module.coffee b/source/module.coffee index 8be9175..4000f80 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -184,7 +184,7 @@ class Space.Module extends Space.Object return config.log or {} _mapSpaceServices: -> - @injector.map('log').toStaticValue(@log) + @injector.map('log').to @log _mapMeteorApis: -> @log.debug("#{@constructor.publishedAs}: _mapMeteorApis") From 37cafb0c31e6a3593889342c733e3ff968aeedaa Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:03:25 +0200 Subject: [PATCH 016/111] Fixes module unit tests --- tests/unit/module.unit.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/module.unit.coffee b/tests/unit/module.unit.coffee index 859ad5f..ea64987 100644 --- a/tests/unit/module.unit.coffee +++ b/tests/unit/module.unit.coffee @@ -103,6 +103,7 @@ describe 'Space.Module - #start', -> beforeEach -> @module = new Space.Module() + @module.log = {debug: sinon.spy()} @module.start() @module._runLifeCycleAction = sinon.spy() @@ -117,6 +118,7 @@ describe 'Space.Module - #stop', -> beforeEach -> @module = new Space.Module() + @module.log = {debug: sinon.spy()} @module.start() @module.stop() @module._runLifeCycleAction = sinon.spy() @@ -132,6 +134,7 @@ describe 'Space.Module - #reset', -> beforeEach -> @module = new Space.Module() + @module.log = {debug: sinon.spy()} @module._runLifeCycleAction = sinon.spy() it.server 'rejects attempts to reset when in production', -> From 319b5916d36c61fcb3ec55231ad64a89f09cd6d0 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:03:42 +0200 Subject: [PATCH 017/111] Refactors Space.Logger unit tests --- tests/unit/logger.tests.js | 267 +++++++++++++++++++++++++------------ 1 file changed, 181 insertions(+), 86 deletions(-) diff --git a/tests/unit/logger.tests.js b/tests/unit/logger.tests.js index acab9e7..0f6416c 100644 --- a/tests/unit/logger.tests.js +++ b/tests/unit/logger.tests.js @@ -1,112 +1,207 @@ -describe("Space.Logger", function() { +Space.Logger.Adapter.extend('TestAdapter', { + Constructor(lib) { + return this.setLib(lib); + }, +}); - beforeEach(function() { - this.log = new Space.Logger(); - }); +describe("Space.Logger", () => { - afterEach(function() { - this.log.stop(); + beforeEach(() => { + this.testLib = { + debug: sinon.spy(), + info: sinon.spy(), + warning: sinon.spy(), + error: sinon.spy() + }; + this.testAdapter = new TestAdapter(this.testLib); + this.log = new Space.Logger(); }); - it('extends Space.Object', function() { + it('extends Space.Object', () => { expect(Space.Logger).to.extend(Space.Object); }); - it("is available of both client and server", function() { + it("is available of both client and server", () => { if (Meteor.isServer || Meteor.isClient) expect(this.log).to.be.instanceOf(Space.Logger); }); - it("only logs after starting", function() { - this.log.start(); - this.log._logger.info = sinon.spy(); - let message = 'My Log Message'; - this.log.info(message); - expect(this.log._logger.info).to.be.calledWithExactly(message); - }); + describe('adapters', () => { + it('throws error if adding adapter without string id', () => { + const adapter = new TestAdapter(sinon.spy()); - it("it can log a debug message to the output channel when min level is equal but not less", function() { - this.log.start(); - this.log.setMinLevel('debug'); - this.log._logger.debug = sinon.spy(); - let message = 'My log message'; - this.log.debug(message); - expect(this.log._logger.debug).to.be.calledWithExactly(message); - this.log._logger.debug = sinon.spy(); - this.log.setMinLevel('info'); - this.log.debug(message); - expect(this.log._logger.debug).not.to.be.called; + const addAdapter = () => this.log.addAdapter(undefined, adapter); + expect(addAdapter).to.throw(Error); + }); + + it('throws error if adding adapter with non-string id', () => { + const adapter = new TestAdapter(sinon.spy()); + + const addAdapter = () => this.log.addAdapter(adapter); + expect(addAdapter).to.throw(Error); + }); + + it('throws error if adding would override adapter', () => { + const adapterId = 'testAdapter'; + const adapter = new TestAdapter(sinon.spy()); + + this.log.addAdapter(adapterId, adapter); + const overrideAdapter = () => this.log.addAdapter(adapterId, adapter); + expect(overrideAdapter).to.throw( + Space.Logger.prototype.ERRORS.mappingExists(adapterId) + ) + }); + + it('throws error if adding adapter without string id', () => { + const adapter = new TestAdapter(sinon.spy()); + + const addAdapter = () => this.log.addAdapter(undefined, adapter); + expect(addAdapter).to.throw(Error); + }); + + it('adds adapter', () => { + const adapterId = 'testAdapter'; + const adapter = new TestAdapter(sinon.spy()); + + this.log.addAdapter(adapterId, adapter); + expect(this.log.adapter(adapterId)).to.equal(adapter); + expect(this.log.existsAdapter(adapterId)).to.be.true; + }); + + it('overrides adapter', () => { + const adapterId = 'testAdapter'; + const adapter = new TestAdapter(sinon.spy()); + const overridingAdapter = new TestAdapter(sinon.spy()); + + this.log.addAdapter(adapterId, adapter); + const overrideAdapter = () => { + this.log.overrideAdapter(adapterId, overridingAdapter); + } + expect(overrideAdapter).to.not.throw(Error); + expect(this.log.adapter(adapterId)).to.equal(overridingAdapter); + }); + + it('resolves adapter by id', () => { + consoleAdapter = new TestAdapter(sinon.spy()); + fileAdapter = new TestAdapter(sinon.spy()); + + this.log.addAdapter('console', consoleAdapter); + this.log.addAdapter('file', fileAdapter); + expect(this.log.adapter('console')).to.equal(consoleAdapter); + expect(this.log.adapter('file')).to.equal(fileAdapter); + expect(this.log.adapter('totallyNotFakeAdapter')).to.be.null; + }); + + it('removes adapter', () => { + const adapterId = 'testAdapter'; + const adapter = new TestAdapter(sinon.spy()); + + this.log.addAdapter(adapterId, adapter); + this.log.removeAdapter(adapterId); + expect(this.log.adapter(adapterId)).to.be.null; + expect(this.log.existsAdapter(adapterId)).to.be.false; + }); + + it('returns adapters', () => { + const adapters = { + console: new TestAdapter(sinon.spy()), + file: new TestAdapter(sinon.spy()) + } + this.log.addAdapter('console', adapters['console']); + this.log.addAdapter('file', adapters['file']); + expect(this.log.adapters()).to.include(adapters); + }); }); - it("it can log an info message to the output channel when min level is equal or higher, but not less", function() { - this.log.start(); - this.log.setMinLevel('info'); - this.log._logger.info = sinon.spy(); - this.log._logger.debug = sinon.spy(); - let message = 'My log message'; - this.log.info(message); - expect(this.log._logger.info).to.be.calledWithExactly(message); - expect(this.log._logger.debug).not.to.be.called; - this.log._logger.info = sinon.spy(); - this.log.setMinLevel('warning'); + it("only logs after starting", () => { + this.log.addAdapter('test', this.testAdapter) + const message = 'My log message'; + + expect(this.log.isRunning()).to.be.false; + expect(this.log.isStopped()).to.be.true; this.log.info(message); - expect(this.log._logger.info).not.to.be.called; - }); + expect(this.testLib.info.calledWith(message)).to.not.true; - it.server("it can log a warning message to the output channel when min level is equal or higher, but not less", function() { this.log.start(); - this.log.setMinLevel('warning'); - this.log._logger.warning = sinon.spy(); - this.log._logger.info = sinon.spy(); - let message = 'My log message'; - this.log.warning(message); - expect(this.log._logger.warning).to.be.calledWithExactly(message); - expect(this.log._logger.info).not.to.be.called; - this.log._logger.warning = sinon.spy(); - this.log.setMinLevel('error'); - this.log.warning(message); - expect(this.log._logger.warning).not.to.be.called; + expect(this.log.isRunning()).to.be.true; + expect(this.log.isStopped()).to.be.false; + this.log.info(message); + expect(this.testLib.info.calledWith(message)).to.be.true; }); - it.client("it can log a warning message to the output channel when min level is equal or higher, but not less", function() { - this.log.start(); - this.log.setMinLevel('warning'); - this.log._logger.warn = sinon.spy(); - this.log._logger.info = sinon.spy(); - let message = 'My log message'; - this.log.warning(message); - expect(this.log._logger.warn).to.be.calledWithExactly(message); - expect(this.log._logger.info).not.to.be.called; - this.log._logger.warn = sinon.spy(); - this.log.setMinLevel('error'); - this.log.warning(message); - expect(this.log._logger.warn).not.to.be.called; - }); + it("allows logging output to be stopped", () => { + this.log.addAdapter('test', this.testAdapter) + const message = 'My log message'; - it("it can log an error message to the output channel when min level is equal", function() { - this.log.start(); - this.log.setMinLevel('error'); - this.log._logger.error = sinon.spy(); - this.log._logger.info = sinon.spy(); - let message = 'My log message'; - this.log.error(message); - expect(this.log._logger.error).to.be.calledWithExactly(message); - expect(this.log._logger.info).not.to.be.called; - this.log._logger.info = sinon.spy(); - this.log.setMinLevel('debug'); - this.log.error(message); - expect(this.log._logger.error).to.be.calledWithExactly(message); - }); + expect(this.log.isRunning()).to.be.false; + expect(this.log.isStopped()).to.be.true; + this.log.start() + expect(this.log.isRunning()).to.be.true; + expect(this.log.isStopped()).to.be.false; + this.log.info(message); + expect(this.testLib.info.calledWith(message)).to.be.true; - it("allows logging output to be stopped", function() { - this.log._logger.info = sinon.spy(); - this.log.start(); - expect(this.log._is('running')).to.be.true; this.log.stop(); - let message = 'My Log Message'; + expect(this.log.isRunning()).to.be.false; + expect(this.log.isStopped()).to.be.true; + this.log.info(message); - expect(this.log._logger.info).not.to.be.called; - expect(this.log._is('stopped')).to.be.true; + expect(this.testLib.info).to.not.be.calledTwice; }); + describe('logging', () => { + it('allows multiple logging adapters to log same message', () => { + const firstLib = {debug: sinon.spy()}; + const firstAdapter = new TestAdapter(firstLib); + const secondLib = {debug: sinon.spy()}; + const secondAdapter = new TestAdapter(secondLib); + const message = 'My log message'; + + this.log.addAdapter('first', firstAdapter); + this.log.addAdapter('second', secondAdapter); + this.log.start(); + + this.log.debug(message); + expect(firstLib.debug.calledWith(message)).to.be.true; + expect(secondLib.debug.calledWith(message)).to.be.true; + }); + + describe('logs message as', () => { + it("debug", () => { + this.log.addAdapter('test', this.testAdapter); + this.log.start(); + + const message = 'My log message'; + this.log.debug(message); + expect(this.testLib.debug.calledWith(message)).to.be.true; + }); + + it("info", () => { + this.log.addAdapter('test', this.testAdapter); + this.log.start(); + + const message = 'My log message'; + this.log.info(message); + expect(this.testLib.info.calledWith(message)).to.be.true; + }); + + it("warning", () => { + this.log.addAdapter('test', this.testAdapter); + this.log.start(); + + const message = 'My log message'; + this.log.warning(message); + expect(this.testLib.warning.calledWith(message)).to.be.true; + }); + + it("error", () => { + this.log.addAdapter('test', this.testAdapter); + this.log.start(); + + const message = 'My log message'; + this.log.error(message); + expect(this.testLib.error.calledWith(message)).to.be.true; + }); + }); + }); }); From fa9c2dc44a749f733c35fe8bf21d3720d918c200 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 May 2016 05:11:41 +0200 Subject: [PATCH 018/111] One semicolon here, one semicolon there... --- source/logger.js | 2 +- tests/unit/logger.tests.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/source/logger.js b/source/logger.js index be47339..b9b7aae 100644 --- a/source/logger.js +++ b/source/logger.js @@ -52,7 +52,7 @@ Space.Object.extend('Space.Logger', { stop() { if (this._is(this.STATES.running)) { - return this._state = this.STATES.stopped;; + return this._state = this.STATES.stopped; } }, diff --git a/tests/unit/logger.tests.js b/tests/unit/logger.tests.js index 0f6416c..3902bcd 100644 --- a/tests/unit/logger.tests.js +++ b/tests/unit/logger.tests.js @@ -49,7 +49,7 @@ describe("Space.Logger", () => { const overrideAdapter = () => this.log.addAdapter(adapterId, adapter); expect(overrideAdapter).to.throw( Space.Logger.prototype.ERRORS.mappingExists(adapterId) - ) + ); }); it('throws error if adding adapter without string id', () => { @@ -76,7 +76,7 @@ describe("Space.Logger", () => { this.log.addAdapter(adapterId, adapter); const overrideAdapter = () => { this.log.overrideAdapter(adapterId, overridingAdapter); - } + }; expect(overrideAdapter).to.not.throw(Error); expect(this.log.adapter(adapterId)).to.equal(overridingAdapter); }); @@ -106,15 +106,15 @@ describe("Space.Logger", () => { const adapters = { console: new TestAdapter(sinon.spy()), file: new TestAdapter(sinon.spy()) - } - this.log.addAdapter('console', adapters['console']); - this.log.addAdapter('file', adapters['file']); + }; + this.log.addAdapter('console', adapters.console); + this.log.addAdapter('file', adapters.file); expect(this.log.adapters()).to.include(adapters); }); }); it("only logs after starting", () => { - this.log.addAdapter('test', this.testAdapter) + this.log.addAdapter('test', this.testAdapter); const message = 'My log message'; expect(this.log.isRunning()).to.be.false; @@ -130,12 +130,12 @@ describe("Space.Logger", () => { }); it("allows logging output to be stopped", () => { - this.log.addAdapter('test', this.testAdapter) + this.log.addAdapter('test', this.testAdapter); const message = 'My log message'; expect(this.log.isRunning()).to.be.false; expect(this.log.isStopped()).to.be.true; - this.log.start() + this.log.start(); expect(this.log.isRunning()).to.be.true; expect(this.log.isStopped()).to.be.false; this.log.info(message); From d886a5bd9059a98da494e476ae53968cd469ca6f Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 04:10:28 +0100 Subject: [PATCH 019/111] Removes back winston dependency from meteor --- package.js | 1 - 1 file changed, 1 deletion(-) diff --git a/package.js b/package.js index ebfffbf..36bb3aa 100644 --- a/package.js +++ b/package.js @@ -8,7 +8,6 @@ Package.describe({ Npm.depends({ "getenv": "0.5.0", - "winston": "2.1.0", "babel-plugin-transform-decorators-legacy": "1.3.4" }); From cbb3ca56cc549769ee2faeb503e82825ffadae52 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 04:17:27 +0100 Subject: [PATCH 020/111] Refactors Logger --- source/logger.js | 90 +++++++------- source/loggers/adapter.js | 48 ++++---- source/loggers/console-adapter.js | 14 ++- tests/unit/logger.tests.js | 188 +++++++++++++++--------------- 4 files changed, 168 insertions(+), 172 deletions(-) diff --git a/source/logger.js b/source/logger.js index b9b7aae..d28ea89 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,4 +1,4 @@ -Space.Object.extend('Space.Logger', { +const Logger = Space.Object.extend('Space.Logger', { STATES: { stopped: 'stopped', @@ -7,96 +7,94 @@ Space.Object.extend('Space.Logger', { Constructor() { this._state = this.STATES.stopped; - return this._adapters = {}; + this._adapters = {}; }, - addAdapter(id, adapter, override) { - if (override == null) { - let override = false; + addAdapter(id, adapter, shouldOverride = false) { + if (!id || typeof id !== 'string') { + throw new Error(this.constructor.ERRORS.invalidId); } - check(id, String); - if (this.existsAdapter(id) && !override) { - throw new Error(this.ERRORS.mappingExists(id)); + if (this.hasAdapter(id) && !shouldOverride) { + throw new Error(this.constructor.ERRORS.mappingExists(id)); } - check(adapter, Space.Logger.Adapter); - return this._adapters[id] = adapter; + this._adapters[id] = adapter; }, - overrideAdapter(id, item) { - return this.addAdapter(id, item, true); + overrideAdapter(id, adapter) { + return this.addAdapter(id, adapter, true); }, - adapter(id) { + getAdapter(id) { return this._adapters[id] || null; }, - existsAdapter(id) { - return (this._adapters[id] != null); + hasAdapter(id) { + return (this._adapters[id] !== null && this._adapters[id] !== undefined); }, removeAdapter(id) { - if (this._adapters[id]) { - return delete this._adapters[id]; - } + if (this._adapters[id]) {delete this._adapters[id];} }, - adapters() { + getAdapters() { return this._adapters; }, start() { - if (this._is(this.STATES.stopped)) { - return this._state = this.STATES.running; + if (this.isInState(this.STATES.stopped)) { + this._state = this.STATES.running; } }, stop() { - if (this._is(this.STATES.running)) { - return this._state = this.STATES.stopped; + if (this.isInState(this.STATES.running)) { + this._state = this.STATES.stopped; } }, - debug(message) { - return this._log('debug', arguments); + debug(...args) { + this._log('debug', args); }, - info(message) { - return this._log('info', arguments); + info(...args) { + this._log('info', args); }, - warning(message) { - return this._log('warning', arguments); + warning(...args) { + this._log('warning', args); }, - error(message) { - return this._log('error', arguments); + error(...args) { + this._log('error', args); }, - _is(expectedState) { + isInState(expectedState) { return (this._state === expectedState); }, isRunning() { - return this._is(this.STATES.running); + return this.isInState(this.STATES.running); }, isStopped() { - return this._is(this.STATES.stopped); + return this.isInState(this.STATES.stopped); }, - _log(level, message) { - if (!this._is(this.STATES.running)) { - return; - } - - _.each(this._adapters, function(adapter, id) { - adapter[level].apply(adapter, message); - }); - }, + _log(level, args) { + if (!this.isInState(this.STATES.running)) {return;} - ERRORS: { - mappingExists(id) { - return `Adapter with id <${id}> would be overwritten. Use method for that`; + for (let adapter of Object.values(this.getAdapters())) { + adapter[level].apply(adapter, args); } } }); + +Logger.ERRORS = { + mappingExists(id) { + return `Adapter with id '${id}' would be overwritten. Use method + 'overrideAdapter' for that`; + }, + invalidId: 'Cannot map or or non string values' +}; + +export default Logger; diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index e6e6f66..6a10d66 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -1,44 +1,44 @@ -Space.Object.extend('Space.Logger.Adapter', { +const LoggingAdapter = Space.Object.extend('Space.Logger.LoggingAdapter', { _lib: null, + Constructor(lib) { + if (!lib) { + throw new Error(this.ERRORS.undefinedLibrary); + } + this.setLibrary(lib); + }, - debug(message) { - check(message, String); - this._log('debug', arguments); + setLibrary(lib) { + this._lib = lib; }, - info(message) { - check(message, String); - this._log('info', arguments); + getLibrary() { + return this._lib || null; }, - warning(message) { - check(message, String); - this._log('warning', arguments); + debug(...args) { + this._log('debug', args); }, - error(message) { - check(message, String); - this._log('error', arguments); + info(...args) { + this._log('info', args); }, - setLib(lib) { - this._lib = lib; + warning(...args) { + this._log('warning', args); }, - lib() { - if (!this._lib) { - throw new Error(this.ERRORS.undefinedLib); - } - return this._lib; + error(...args) { + this._log('error', args); }, - _log(level, message) { - this._lib[level].apply(this._lib, message); + _log(level, args) { + this._lib[level].apply(this._lib, args); }, ERRORS: { - undefinedLib: 'Logging library is not set on adapter' + undefinedLibrary: 'Logging library is required' } +}); -}); \ No newline at end of file +export default LoggingAdapter; diff --git a/source/loggers/console-adapter.js b/source/loggers/console-adapter.js index 5b86444..4d28e0a 100644 --- a/source/loggers/console-adapter.js +++ b/source/loggers/console-adapter.js @@ -1,12 +1,14 @@ -Space.Logger.Adapter.extend('Space.Logger.ConsoleAdapter', { +import LoggingAdapter from './adapter'; + +const ConsoleLogger = LoggingAdapter.extend('Space.Logger.ConsoleAdapter', { Constructor() { - return this.setLib(console); + LoggingAdapter.call(this, console); }, - warning(message) { - check(message, String); - return this._log('warn', arguments); + warning(...args) { + return this._log('warn', args); } +}); -}); \ No newline at end of file +export default ConsoleLogger; diff --git a/tests/unit/logger.tests.js b/tests/unit/logger.tests.js index 3902bcd..475e925 100644 --- a/tests/unit/logger.tests.js +++ b/tests/unit/logger.tests.js @@ -1,105 +1,98 @@ -Space.Logger.Adapter.extend('TestAdapter', { +import Logger from '../../source/logger.js'; +import LoggingAdapter from '../../source/loggers/adapter.js'; + +const TestAdapter = LoggingAdapter.extend('TestAdapter', { Constructor(lib) { - return this.setLib(lib); - }, + return this.setLibrary(lib); + } }); -describe("Space.Logger", () => { +describe("Logger", function() { beforeEach(() => { - this.testLib = { + this.lib = { debug: sinon.spy(), info: sinon.spy(), warning: sinon.spy(), error: sinon.spy() }; - this.testAdapter = new TestAdapter(this.testLib); - this.log = new Space.Logger(); + this.testAdapter = new TestAdapter(this.lib); + this.logger = new Logger(); }); it('extends Space.Object', () => { - expect(Space.Logger).to.extend(Space.Object); + expect(Logger).to.extend(Space.Object); }); it("is available of both client and server", () => { - if (Meteor.isServer || Meteor.isClient) - expect(this.log).to.be.instanceOf(Space.Logger); + expect(this.logger).to.be.instanceOf(Logger); }); describe('adapters', () => { - it('throws error if adding adapter without string id', () => { + it('throws error if id does not exists', () => { const adapter = new TestAdapter(sinon.spy()); - - const addAdapter = () => this.log.addAdapter(undefined, adapter); - expect(addAdapter).to.throw(Error); + expect(() => this.logger.addAdapter(undefined, adapter)).to.throw( + Logger.ERRORS.invalidId + ); }); - it('throws error if adding adapter with non-string id', () => { + it('throws error if id is not a string value', () => { const adapter = new TestAdapter(sinon.spy()); - - const addAdapter = () => this.log.addAdapter(adapter); - expect(addAdapter).to.throw(Error); + expect(() => this.logger.addAdapter(adapter)).to.throw( + Logger.ERRORS.invalidId + ); }); - it('throws error if adding would override adapter', () => { + it('throws error if adapter would be overridden', () => { const adapterId = 'testAdapter'; const adapter = new TestAdapter(sinon.spy()); - this.log.addAdapter(adapterId, adapter); - const overrideAdapter = () => this.log.addAdapter(adapterId, adapter); - expect(overrideAdapter).to.throw( - Space.Logger.prototype.ERRORS.mappingExists(adapterId) + this.logger.addAdapter(adapterId, adapter); + expect(() => this.logger.addAdapter(adapterId, adapter)).to.throw( + Logger.ERRORS.mappingExists(adapterId) ); }); - it('throws error if adding adapter without string id', () => { - const adapter = new TestAdapter(sinon.spy()); - - const addAdapter = () => this.log.addAdapter(undefined, adapter); - expect(addAdapter).to.throw(Error); - }); - it('adds adapter', () => { const adapterId = 'testAdapter'; const adapter = new TestAdapter(sinon.spy()); - this.log.addAdapter(adapterId, adapter); - expect(this.log.adapter(adapterId)).to.equal(adapter); - expect(this.log.existsAdapter(adapterId)).to.be.true; + this.logger.addAdapter(adapterId, adapter); + expect(this.logger.getAdapter(adapterId)).to.equal(adapter); + expect(this.logger.hasAdapter(adapterId)).to.be.true; }); - it('overrides adapter', () => { + it('allows to override adapter', () => { const adapterId = 'testAdapter'; const adapter = new TestAdapter(sinon.spy()); const overridingAdapter = new TestAdapter(sinon.spy()); - this.log.addAdapter(adapterId, adapter); - const overrideAdapter = () => { - this.log.overrideAdapter(adapterId, overridingAdapter); - }; - expect(overrideAdapter).to.not.throw(Error); - expect(this.log.adapter(adapterId)).to.equal(overridingAdapter); + this.logger.addAdapter(adapterId, adapter); + expect(() => { + this.logger.overrideAdapter(adapterId, overridingAdapter); + }).to.not.throw(Error); + expect(this.logger.getAdapter(adapterId)).to.equal(overridingAdapter); }); it('resolves adapter by id', () => { consoleAdapter = new TestAdapter(sinon.spy()); fileAdapter = new TestAdapter(sinon.spy()); - this.log.addAdapter('console', consoleAdapter); - this.log.addAdapter('file', fileAdapter); - expect(this.log.adapter('console')).to.equal(consoleAdapter); - expect(this.log.adapter('file')).to.equal(fileAdapter); - expect(this.log.adapter('totallyNotFakeAdapter')).to.be.null; + this.logger.addAdapter('console', consoleAdapter); + this.logger.addAdapter('file', fileAdapter); + expect(this.logger.getAdapter('console')).to.equal(consoleAdapter); + expect(this.logger.getAdapter('file')).to.equal(fileAdapter); + expect(this.logger.getAdapter('non-existing-adapter')).to.be.null; }); it('removes adapter', () => { const adapterId = 'testAdapter'; const adapter = new TestAdapter(sinon.spy()); - this.log.addAdapter(adapterId, adapter); - this.log.removeAdapter(adapterId); - expect(this.log.adapter(adapterId)).to.be.null; - expect(this.log.existsAdapter(adapterId)).to.be.false; + this.logger.addAdapter(adapterId, adapter); + this.logger.removeAdapter(adapterId); + expect(this.logger.getAdapter(adapterId)).to.be.null; + expect(this.logger.hasAdapter(adapterId)).to.be.false; }); it('returns adapters', () => { @@ -107,46 +100,47 @@ describe("Space.Logger", () => { console: new TestAdapter(sinon.spy()), file: new TestAdapter(sinon.spy()) }; - this.log.addAdapter('console', adapters.console); - this.log.addAdapter('file', adapters.file); - expect(this.log.adapters()).to.include(adapters); + this.logger.addAdapter('console', adapters.console); + this.logger.addAdapter('file', adapters.file); + expect(this.logger.getAdapters()).to.be.eql(adapters); }); }); it("only logs after starting", () => { - this.log.addAdapter('test', this.testAdapter); + this.logger.addAdapter('my-logger', this.testAdapter); const message = 'My log message'; - expect(this.log.isRunning()).to.be.false; - expect(this.log.isStopped()).to.be.true; - this.log.info(message); - expect(this.testLib.info.calledWith(message)).to.not.true; - - this.log.start(); - expect(this.log.isRunning()).to.be.true; - expect(this.log.isStopped()).to.be.false; - this.log.info(message); - expect(this.testLib.info.calledWith(message)).to.be.true; + expect(this.logger.isRunning()).to.be.false; + expect(this.logger.isStopped()).to.be.true; + this.logger.info(message); + expect(this.lib.info).to.not.be.called; + + this.logger.start(); + expect(this.logger.isRunning()).to.be.true; + expect(this.logger.isStopped()).to.be.false; + this.logger.info(message); + expect(this.lib.info).to.be.calledOnce; + expect(this.lib.info.calledWith(message)).to.be.true; }); it("allows logging output to be stopped", () => { - this.log.addAdapter('test', this.testAdapter); + this.logger.addAdapter('my-logger', this.testAdapter); const message = 'My log message'; - expect(this.log.isRunning()).to.be.false; - expect(this.log.isStopped()).to.be.true; - this.log.start(); - expect(this.log.isRunning()).to.be.true; - expect(this.log.isStopped()).to.be.false; - this.log.info(message); - expect(this.testLib.info.calledWith(message)).to.be.true; + expect(this.logger.isRunning()).to.be.false; + expect(this.logger.isStopped()).to.be.true; + this.logger.start(); + expect(this.logger.isRunning()).to.be.true; + expect(this.logger.isStopped()).to.be.false; + this.logger.info(message); + expect(this.lib.info.calledWith(message)).to.be.true; - this.log.stop(); - expect(this.log.isRunning()).to.be.false; - expect(this.log.isStopped()).to.be.true; + this.logger.stop(); + expect(this.logger.isRunning()).to.be.false; + expect(this.logger.isStopped()).to.be.true; - this.log.info(message); - expect(this.testLib.info).to.not.be.calledTwice; + this.logger.info(message); + expect(this.lib.info).to.not.be.calledTwice; }); describe('logging', () => { @@ -157,50 +151,52 @@ describe("Space.Logger", () => { const secondAdapter = new TestAdapter(secondLib); const message = 'My log message'; - this.log.addAdapter('first', firstAdapter); - this.log.addAdapter('second', secondAdapter); - this.log.start(); + this.logger.addAdapter('first', firstAdapter); + this.logger.addAdapter('second', secondAdapter); + this.logger.start(); - this.log.debug(message); + this.logger.debug(message); expect(firstLib.debug.calledWith(message)).to.be.true; + expect(firstLib.debug).to.be.calledOnce; expect(secondLib.debug.calledWith(message)).to.be.true; + expect(secondLib.debug).to.be.calledOnce; }); describe('logs message as', () => { it("debug", () => { - this.log.addAdapter('test', this.testAdapter); - this.log.start(); + this.logger.addAdapter('my-logger', this.testAdapter); + this.logger.start(); const message = 'My log message'; - this.log.debug(message); - expect(this.testLib.debug.calledWith(message)).to.be.true; + this.logger.debug(message); + expect(this.lib.debug.calledWith(message)).to.be.true; }); it("info", () => { - this.log.addAdapter('test', this.testAdapter); - this.log.start(); + this.logger.addAdapter('my-logger', this.testAdapter); + this.logger.start(); const message = 'My log message'; - this.log.info(message); - expect(this.testLib.info.calledWith(message)).to.be.true; + this.logger.info(message); + expect(this.lib.info.calledWith(message)).to.be.true; }); it("warning", () => { - this.log.addAdapter('test', this.testAdapter); - this.log.start(); + this.logger.addAdapter('my-logger', this.testAdapter); + this.logger.start(); const message = 'My log message'; - this.log.warning(message); - expect(this.testLib.warning.calledWith(message)).to.be.true; + this.logger.warning(message); + expect(this.lib.warning.calledWith(message)).to.be.true; }); it("error", () => { - this.log.addAdapter('test', this.testAdapter); - this.log.start(); + this.logger.addAdapter('my-logger', this.testAdapter); + this.logger.start(); const message = 'My log message'; - this.log.error(message); - expect(this.testLib.error.calledWith(message)).to.be.true; + this.logger.error(message); + expect(this.lib.error.calledWith(message)).to.be.true; }); }); }); From 07e58065ef41b78abe82ef7647d659626ee9dad3 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 05:50:29 +0100 Subject: [PATCH 021/111] Updates .eslintrc --- .eslintrc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.eslintrc b/.eslintrc index 4fd6382..78c0eb9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -167,11 +167,10 @@ "before": false, "after": true }], - "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords + "keyword-spacing": 2, // http://eslint.org/docs/rules/space-after-keywords "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops - "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case "spaced-comment": 2, // http://eslint.org/docs/rules/spaced-comment (previously known as spaced-line-comment) } -} +} \ No newline at end of file From 4118033459eb73341c7b96c079f0d50e56e5bb0c Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 05:53:33 +0100 Subject: [PATCH 022/111] Adds package.json --- package.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..4a7a7d5 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "space-base", + "version": "4.1.4", + "description": "Modular Application Architecture", + "dependencies": { + "getenv": "0.7.x", + "underscore": "1.8.3", + "simplecheck": "git://github.com/meteor-space/base.git" + }, + "devDependencies": {}, + "scripts": {}, + "author": "Dominik Guzei, Rhys Bartels-Waller, Darko Mijic, Adam Desivi", + "license": "MIT" +} \ No newline at end of file From 8b916fe696ad8b533bb008fb93eae2635e1e37dd Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 05:59:14 +0100 Subject: [PATCH 023/111] Removes tightly coupled Meteor deps --- package.js | 36 +----- source/module.coffee | 42 ------- .../standalone_application.integration.coffee | 107 ------------------ 3 files changed, 2 insertions(+), 183 deletions(-) delete mode 100644 tests/integration/standalone_application.integration.coffee diff --git a/package.js b/package.js index 36bb3aa..a9115f9 100644 --- a/package.js +++ b/package.js @@ -7,35 +7,18 @@ Package.describe({ }); Npm.depends({ - "getenv": "0.5.0", "babel-plugin-transform-decorators-legacy": "1.3.4" }); Package.onUse(function(api) { - api.versionsFrom('1.2.0.1'); + api.versionsFrom('1.4.2'); api.use([ 'coffeescript', - 'check', - 'underscore', 'ecmascript' ]); - api.use([ - 'ejson', - 'ddp', - 'random', - 'mongo', - 'tracker', - 'templating', - 'session', - 'blaze', - 'email', - 'accounts-base', - 'reactive-var' - ], {weak: true}); - api.addFiles([ 'source/lib/underscore_deep_extend_mixin.js', 'source/namespace.coffee', @@ -50,7 +33,7 @@ Package.onUse(function(api) { 'source/module.coffee', 'source/application.coffee', 'source/loggers/adapter.js', - 'source/loggers/console-adapter.js', + 'source/loggers/console-adapter.js' ]); // Test helpers @@ -63,24 +46,11 @@ Package.onUse(function(api) { Package.onTest(function(api) { api.use([ - 'meteor', 'coffeescript', - 'check', 'ecmascript', 'space:base', // weak-dependencies - 'ddp', - 'random', - 'underscore', - 'mongo', - 'tracker', - 'templating', - 'ejson', - 'accounts-base', - 'email', - 'session', - 'reactive-var', 'practicalmeteor:munit@2.1.5', 'space:testing@3.0.1' ]); @@ -100,10 +70,8 @@ Package.onTest(function(api) { // integration tests 'tests/integration/application_with_modules.spec.js', - 'tests/integration/standalone_application.integration.coffee', 'tests/integration/lifecycle_hooks.tests.js', 'tests/integration/requiring-modules.tests.js', 'tests/integration/module.regressions.js' ]); - }); diff --git a/source/module.coffee b/source/module.coffee index 4000f80..0254d33 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -33,7 +33,6 @@ class Space.Module extends Space.Object unless isSubModule @injector.map('Injector').to @injector @_mapSpaceServices() - @_mapMeteorApis() # Setup required modules for moduleId in @requiredModules @@ -185,44 +184,3 @@ class Space.Module extends Space.Object _mapSpaceServices: -> @injector.map('log').to @log - - _mapMeteorApis: -> - @log.debug("#{@constructor.publishedAs}: _mapMeteorApis") - # Map Meteor standard packages - @injector.map('Meteor').to Meteor - if Package.ejson? - @injector.map('EJSON').to Package.ejson.EJSON - if Package.ddp? - @injector.map('DDP').to Package.ddp.DDP - if Package.random? - @injector.map('Random').to Package.random.Random - @injector.map('underscore').to Package.underscore._ - if Package.mongo? - @injector.map('Mongo').to Package.mongo.Mongo - if Meteor.isServer - @injector.map('MongoInternals').to Package.mongo.MongoInternals - - if Meteor.isClient - if Package.tracker? - @injector.map('Tracker').to Package.tracker.Tracker - if Package.templating? - @injector.map('Template').to Package.templating.Template - if Package.session? - @injector.map('Session').to Package.session.Session - if Package.blaze? - @injector.map('Blaze').to Package.blaze.Blaze - - if Meteor.isServer - @injector.map('check').to check - @injector.map('Match').to Match - @injector.map('process').to process - @injector.map('Future').to Npm.require 'fibers/future' - - if Package.email? - @injector.map('Email').to Package.email.Email - - if Package['accounts-base']? - @injector.map('Accounts').to Package['accounts-base'].Accounts - - if Package['reactive-var']? - @injector.map('ReactiveVar').to Package['reactive-var'].ReactiveVar diff --git a/tests/integration/standalone_application.integration.coffee b/tests/integration/standalone_application.integration.coffee deleted file mode 100644 index 4a78345..0000000 --- a/tests/integration/standalone_application.integration.coffee +++ /dev/null @@ -1,107 +0,0 @@ - -describe 'Meteor integration in applications', -> - - it 'maps Meteor core packages into the Space environment', -> - - class SharedApp extends Space.Application - - dependencies: - meteor: 'Meteor' - ejson: 'EJSON' - ddp: 'DDP' - accounts: 'Accounts' - random: 'Random' - underscore: 'underscore' - reactiveVar: 'ReactiveVar' - mongo: 'Mongo' - - onInitialize: -> - expect(@meteor).to.be.defined - expect(@meteor).to.equal Meteor - - expect(@ejson).to.be.defined - expect(@ejson).to.equal EJSON - - expect(@ddp).to.be.defined - expect(@ddp).to.equal DDP - - expect(@accounts).to.be.defined - expect(@accounts).to.equal Package['accounts-base'].Accounts - - expect(@random).to.be.defined - expect(@random).to.equal Random - - expect(@underscore).to.be.defined - expect(@underscore).to.equal Package.underscore._ - - expect(@reactiveVar).to.equal Package['reactive-var'].ReactiveVar - - expect(@mongo).to.be.defined - expect(@mongo).to.equal Mongo - - new SharedApp() - - # CLIENT ONLY - - if Meteor.isClient - - class ClientApp extends Space.Application - - dependencies: - tracker: 'Tracker' - templates: 'Template' - session: 'Session' - blaze: 'Blaze' - - onInitialize: -> - - expect(@tracker).to.be.defined - expect(@tracker).to.equal Tracker - - expect(@templates).to.be.defined - expect(@templates).to.equal Template - - expect(@session).to.be.defined - expect(@session).to.equal Session - - expect(@blaze).to.be.defined - expect(@blaze).to.equal Blaze - - new ClientApp() - - # SERVER ONLY - - if Meteor.isServer - - class ServerApp extends Space.Application - - dependencies: - email: 'Email' - process: 'process' - Future: 'Future' - mongoInternals: 'MongoInternals' - - onInitialize: -> - expect(@email).to.be.defined - expect(@email).to.equal Package['email'].Email - expect(@process).to.be.defined - expect(@process).to.equal process - expect(@Future).to.be.defined - expect(@Future).to.equal Npm.require 'fibers/future' - expect(@mongoInternals).to.be.defined - expect(@mongoInternals).to.equal MongoInternals - - new ServerApp() - - it 'boots core Space Services', -> - - class SharedApp extends Space.Application - - dependencies: - log: 'log' - - onInitialize: -> - expect(@log).to.be.defined - expect(@log).to.be.instanceOf Space.Logger - - new SharedApp() From 63bbea2ae88aa6dc92c3f13f0de0fd906e37843c Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 7 Jan 2017 06:01:52 +0100 Subject: [PATCH 024/111] Replaces used Meteor deps in favor of importable npm packages underscore, check -> simplecheck --- source/application.coffee | 1 + source/error.js | 7 +++++-- source/injector.coffee | 1 + source/injector_annotations.coffee | 2 ++ source/lib/underscore_deep_extend_mixin.js | 2 ++ source/module.coffee | 1 + source/object.coffee | 10 ++++++---- source/struct.coffee | 4 +++- tests/integration/lifecycle_hooks.tests.js | 2 ++ tests/unit/struct.unit.coffee | 17 +++++++++-------- 10 files changed, 32 insertions(+), 15 deletions(-) diff --git a/source/application.coffee b/source/application.coffee index dbef815..112cc3d 100644 --- a/source/application.coffee +++ b/source/application.coffee @@ -1,3 +1,4 @@ +import _ from 'underscore'; class Space.Application extends Space.Module diff --git a/source/error.js b/source/error.js index 782089a..e6138ff 100644 --- a/source/error.js +++ b/source/error.js @@ -1,3 +1,6 @@ +import _ from 'underscore'; +import {optional, Integer} from 'simplecheck'; + let IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; @@ -28,8 +31,8 @@ _.extend( _.extend(fields, { name: String, message: String, - stack: Match.Optional(String), - code: Match.Optional(Match.Integer) + stack: optional(String), + code: optional(Integer) }); return fields; }, diff --git a/source/injector.coffee b/source/injector.coffee index 0086bad..1db270b 100644 --- a/source/injector.coffee +++ b/source/injector.coffee @@ -1,3 +1,4 @@ +import _ from 'underscore'; Space.Error.extend(Space, 'InjectionError') diff --git a/source/injector_annotations.coffee b/source/injector_annotations.coffee index 8ab6eb1..09b5c87 100644 --- a/source/injector_annotations.coffee +++ b/source/injector_annotations.coffee @@ -1,3 +1,5 @@ +import _ from 'underscore'; + @Space.Dependency = (propertyName, dependencyId) -> if (typeof dependencyId == 'undefined') dependencyId = propertyName diff --git a/source/lib/underscore_deep_extend_mixin.js b/source/lib/underscore_deep_extend_mixin.js index 7beb853..0fae5c8 100644 --- a/source/lib/underscore_deep_extend_mixin.js +++ b/source/lib/underscore_deep_extend_mixin.js @@ -1,3 +1,5 @@ +import _ from 'underscore'; + // Deep object extend for underscore // As found on http://stackoverflow.com/a/29563346 diff --git a/source/module.coffee b/source/module.coffee index 0254d33..e32bab4 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -1,3 +1,4 @@ +import _ from 'underscore'; class Space.Module extends Space.Object diff --git a/source/object.coffee b/source/object.coffee index d4e5808..229ac94 100644 --- a/source/object.coffee +++ b/source/object.coffee @@ -1,3 +1,5 @@ +import _ from 'underscore'; +import {ensure, matches, oneOf, optional, anything} from 'simplecheck'; class Space.Object @@ -128,10 +130,10 @@ class Space.Object className = args[1] extension = args[2] - check namespace, Match.OneOf(Match.ObjectIncluding({}), Space.Namespace, Function) - check classPath, Match.OneOf(String, null) - check className, String - check extension, Match.ObjectIncluding({}) + ensure namespace, oneOf(anything, Space.Namespace, Function) + ensure classPath, oneOf(String, null) + ensure className, String + ensure extension, anything # Assign the optional custom constructor for this class Parent = this diff --git a/source/struct.coffee b/source/struct.coffee index 899d546..bda6d56 100644 --- a/source/struct.coffee +++ b/source/struct.coffee @@ -1,3 +1,5 @@ +import {ensure} from 'simplecheck'; +import _ from 'underscore'; class Space.Struct extends Space.Object @@ -15,4 +17,4 @@ class Space.Struct extends Space.Object return copy # Use the fields configuration to check given data during runtime - _checkFields: (data) -> check data, @fields() \ No newline at end of file + _checkFields: (data) -> ensure data, @fields() \ No newline at end of file diff --git a/tests/integration/lifecycle_hooks.tests.js b/tests/integration/lifecycle_hooks.tests.js index 4aa44a0..d65faec 100644 --- a/tests/integration/lifecycle_hooks.tests.js +++ b/tests/integration/lifecycle_hooks.tests.js @@ -1,3 +1,5 @@ +import _ from 'underscore'; + describe("Space.base - Application lifecycle hooks", function() { // TEST HELPERS diff --git a/tests/unit/struct.unit.coffee b/tests/unit/struct.unit.coffee index be96747..fb7b4f0 100644 --- a/tests/unit/struct.unit.coffee +++ b/tests/unit/struct.unit.coffee @@ -1,15 +1,16 @@ +import {MatchError, Integer} from 'simplecheck'; describe 'Space.Struct', -> class MyTestStruct extends Space.Struct @type 'MyTestStruct' - fields: -> name: String, age: Match.Integer + fields: -> name: String, age: Integer class MyExtendedTestStruct extends MyTestStruct @type 'MyExtendedTestStruct' fields: -> fields = super() - fields.extra = Match.Integer + fields.extra = Integer return fields it "is a Space.Object", -> @@ -39,26 +40,26 @@ describe 'Space.Struct', -> expect(copy).not.to.be.instanceof MyTestStruct it 'throws a match error if a property is of wrong type', -> - expect(-> new MyTestStruct name: 5, age: 26).to.throw Match.Error + expect(-> new MyTestStruct name: 5, age: 26).to.throw MatchError it 'throws a match error if additional properties are given', -> - expect(-> new MyTestStruct name: 5, age: 26, extra: 0).to.throw Match.Error + expect(-> new MyTestStruct name: 5, age: 26, extra: 0).to.throw MatchError it 'throws a match error if a property is missing', -> - expect(-> new MyTestStruct name: 5).to.throw Match.Error + expect(-> new MyTestStruct name: 5).to.throw MatchError it 'allows to extend the fields of base classes', -> expect(-> new MyExtendedTestStruct name: 'test', age: 26, extra: 0) - .not.to.throw Match.Error + .not.to.throw MatchError # TODO: remove when breaking change is made for next major version: it 'stays backward compatible with static fields api', -> class StaticFieldsStruct extends Space.Struct - @fields: { name: String, age: Match.Integer } + @fields: { name: String, age: Integer } properties = name: 'Dominik', age: 26 instance = new StaticFieldsStruct properties expect(instance).toMatch properties - expect(-> new StaticFieldsStruct name: 5).to.throw Match.Error + expect(-> new StaticFieldsStruct name: 5).to.throw MatchError From f46f9d8874b44df21c459c9571ae0c1a6c7597a8 Mon Sep 17 00:00:00 2001 From: qejk Date: Mon, 9 Jan 2017 12:14:02 +0100 Subject: [PATCH 025/111] Exports Space modules --- source/application.coffee | 2 ++ source/error.js | 2 ++ source/injector.coffee | 7 ++++--- source/module.coffee | 5 ++++- source/namespace.coffee | 4 +++- source/object.coffee | 6 ++++-- source/struct.coffee | 6 ++++-- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/source/application.coffee b/source/application.coffee index 112cc3d..92ce1a5 100644 --- a/source/application.coffee +++ b/source/application.coffee @@ -19,3 +19,5 @@ class Space.Application extends Space.Module # Make it possible to override configuration (at any nested level) configure: (options) -> _.deepExtend @configuration, options + +export default Space.Application; \ No newline at end of file diff --git a/source/error.js b/source/error.js index e6138ff..d432e1e 100644 --- a/source/error.js +++ b/source/error.js @@ -50,3 +50,5 @@ _.extend( _.extend(Space.Error, _.omit(Space.Object, 'toString'), { __keepToStringMethod__: true // Do not override #toString method }); + +export default Space.Error; diff --git a/source/injector.coffee b/source/injector.coffee index 1db270b..2ec284b 100644 --- a/source/injector.coffee +++ b/source/injector.coffee @@ -1,8 +1,8 @@ import _ from 'underscore'; -Space.Error.extend(Space, 'InjectionError') +InjectionError = Space.Error.extend('Space.InjectionError') -class Space.Injector +Injector = class Space.Injector ERRORS: { cannotMapUndefinedId: -> 'Cannot map or .' @@ -200,7 +200,6 @@ class SingletonProvider extends Provider return @_singleton Space.Injector.DEFAULT_PROVIDERS = - to: ValueProvider toStaticValue: ValueProvider asStaticValue: ValueProvider @@ -208,3 +207,5 @@ Space.Injector.DEFAULT_PROVIDERS = toInstancesOf: InstanceProvider asSingleton: SingletonProvider toSingleton: SingletonProvider + +export {InjectionError as InjectionError, Injector as Injector}; \ No newline at end of file diff --git a/source/module.coffee b/source/module.coffee index e32bab4..cc3fd77 100644 --- a/source/module.coffee +++ b/source/module.coffee @@ -1,4 +1,5 @@ import _ from 'underscore'; +import Logger from './logger.js'; class Space.Module extends Space.Object @@ -173,7 +174,7 @@ class Space.Module extends Space.Object _setupLogger: -> config = @_loggingConfig(@configuration) - logger = new Space.Logger() + logger = new Logger() logger.start() if config.enabled == true return logger @@ -185,3 +186,5 @@ class Space.Module extends Space.Object _mapSpaceServices: -> @injector.map('log').to @log + +export default Space.Module; \ No newline at end of file diff --git a/source/namespace.coffee b/source/namespace.coffee index 078af59..e6c540d 100644 --- a/source/namespace.coffee +++ b/source/namespace.coffee @@ -6,4 +6,6 @@ class Namespace # Define global namespace for the space framework @Space = new Namespace 'Space' @Space.Namespace = Namespace -@Space.registry = {} \ No newline at end of file +@Space.registry = {} + +export default Space; \ No newline at end of file diff --git a/source/object.coffee b/source/object.coffee index 229ac94..e470ed6 100644 --- a/source/object.coffee +++ b/source/object.coffee @@ -1,5 +1,5 @@ import _ from 'underscore'; -import {ensure, matches, oneOf, optional, anything} from 'simplecheck'; +import {ensure, oneOf, anything} from 'simplecheck'; class Space.Object @@ -302,4 +302,6 @@ class Space.Object else value = _.clone(value) if isPlainObject(value) # Set non-existing props and override existing methods - prototype[key] = value \ No newline at end of file + prototype[key] = value + +export default Space.Object; \ No newline at end of file diff --git a/source/struct.coffee b/source/struct.coffee index bda6d56..ed4b29b 100644 --- a/source/struct.coffee +++ b/source/struct.coffee @@ -1,5 +1,5 @@ -import {ensure} from 'simplecheck'; import _ from 'underscore'; +import {ensure} from 'simplecheck'; class Space.Struct extends Space.Object @@ -17,4 +17,6 @@ class Space.Struct extends Space.Object return copy # Use the fields configuration to check given data during runtime - _checkFields: (data) -> ensure data, @fields() \ No newline at end of file + _checkFields: (data) -> ensure data, @fields() + +export default Space.Struct; From 5a0c432544dbcf175276d20465360209b08213fd Mon Sep 17 00:00:00 2001 From: qejk Date: Mon, 9 Jan 2017 12:53:09 +0100 Subject: [PATCH 026/111] Imports Space modules on tests --- .../application_with_modules.spec.js | 12 ++-- tests/integration/lifecycle_hooks.tests.js | 10 +-- tests/integration/module.regressions.js | 13 ++-- tests/integration/requiring-modules.tests.js | 10 +-- tests/unit/application.unit.coffee | 41 ++++++------ tests/unit/error.tests.js | 8 ++- tests/unit/helpers.unit.coffee | 1 + tests/unit/injector.unit.coffee | 23 +++---- tests/unit/injector_annotations.unit.js | 3 +- tests/unit/module.unit.coffee | 59 ++++++++--------- tests/unit/object.unit.coffee | 63 ++++++++++--------- tests/unit/struct.unit.coffee | 17 ++--- 12 files changed, 142 insertions(+), 118 deletions(-) diff --git a/tests/integration/application_with_modules.spec.js b/tests/integration/application_with_modules.spec.js index 5e5f24e..006e311 100644 --- a/tests/integration/application_with_modules.spec.js +++ b/tests/integration/application_with_modules.spec.js @@ -1,8 +1,10 @@ +import Module from '../../source/module.coffee'; +import Application from '../../source/application.coffee'; describe('Building applications based on modules', function() { beforeEach(function() { - Space.Module.published = {}; // reset published space modules + Module.published = {}; // reset published space modules }); it('loads required module correctly', function() { @@ -10,13 +12,13 @@ describe('Building applications based on modules', function() { let testValue = {}; let testResult = null; - Space.Module.define('FirstModule', { + Module.define('FirstModule', { onInitialize: function() { this.injector.map('testValue').to(testValue); } }); - Space.Application.create({ + Application.create({ requiredModules: ['FirstModule'], dependencies: { testValue: 'testValue' }, onInitialize: function() { testResult = this.testValue; } @@ -31,7 +33,7 @@ describe('Building applications based on modules', function() { let appValue = 'application configuration'; let testResult = null; - Space.Module.define('FirstModule', { + Module.define('FirstModule', { onInitialize: function() { this.injector.map('moduleValue').to(moduleValue); }, @@ -40,7 +42,7 @@ describe('Building applications based on modules', function() { } }); - let app = Space.Application.create({ + let app = Application.create({ requiredModules: ['FirstModule'], dependencies: { moduleValue: 'moduleValue' }, onInitialize: function() { diff --git a/tests/integration/lifecycle_hooks.tests.js b/tests/integration/lifecycle_hooks.tests.js index d65faec..bee945b 100644 --- a/tests/integration/lifecycle_hooks.tests.js +++ b/tests/integration/lifecycle_hooks.tests.js @@ -1,4 +1,6 @@ import _ from 'underscore'; +import Module from '../../source/module.coffee'; +import Application from '../../source/application.coffee'; describe("Space.base - Application lifecycle hooks", function() { @@ -48,15 +50,15 @@ describe("Space.base - Application lifecycle hooks", function() { beforeEach(function() { // reset published space modules - Space.Module.published = {}; + Module.published = {}; // Setup lifecycle hooks with sinon spys this.firstHooks = createLifeCycleHookSpies(); this.secondHooks = createLifeCycleHookSpies(); this.appHooks = createLifeCycleHookSpies(); // Create a app setup with two modules and use the spied apon hooks - Space.Module.define('First', this.firstHooks); - Space.Module.define('Second', _.extend(this.secondHooks, { requiredModules: ['First'] })); - this.app = Space.Application.create(_.extend(this.appHooks, { requiredModules: ['Second'] })); + Module.define('First', this.firstHooks); + Module.define('Second', _.extend(this.secondHooks, { requiredModules: ['First'] })); + this.app = Application.create(_.extend(this.appHooks, { requiredModules: ['Second'] })); }); it("runs the initialize hooks in correct order", function() { diff --git a/tests/integration/module.regressions.js b/tests/integration/module.regressions.js index f778760..cd80255 100644 --- a/tests/integration/module.regressions.js +++ b/tests/integration/module.regressions.js @@ -1,18 +1,23 @@ -describe("Space.Module - regressions", function() { +import Module from '../../source/module.coffee'; +import SpaceObject from '../../source/object.coffee'; +import {Injector} from '../../source/injector.coffee'; + +describe("Module - regressions", function() { it("ensures autoboot singletons have access to injector mappings made in module onInitialize", function() { Test = Space.namespace('Test'); + SomeLib = { libMethod: function() {} }; let singletonReadySpy = sinon.spy(); - let myInjector = new Space.Injector(); + let myInjector = new Injector(); - Space.Object.extend(Test, 'MySingleton', { + SpaceObject.extend(Test, 'MySingleton', { dependencies: { someLib: 'SomeLib' }, onDependenciesReady: singletonReadySpy }); - Test.MyModule = Space.Module.extend(Test, 'MyModule', { + Test.MyModule = Module.extend(Test, 'MyModule', { singletons: ['Test.MySingleton'], onInitialize() { this.injector.map('SomeLib').to(SomeLib); } }); diff --git a/tests/integration/requiring-modules.tests.js b/tests/integration/requiring-modules.tests.js index e79bc18..9b31806 100644 --- a/tests/integration/requiring-modules.tests.js +++ b/tests/integration/requiring-modules.tests.js @@ -1,19 +1,21 @@ +import Module from '../../source/module.coffee'; +import Application from '../../source/application.coffee'; describe("Space.base - Requiring modules in other modules and apps", function() { it("multiple modules should be able to require the same base module", function() { - Space.Module.define('BaseModule', { + Module.define('BaseModule', { // Regression test -> this was invoked twice at some point afterInitialize: function() { this.injector.map('x').to('y'); } }); - Space.Module.define('DependentModule1', { requiredModules: ['BaseModule'] }); - Space.Module.define('DependentModule2', { requiredModules: ['BaseModule'] }); + Module.define('DependentModule1', { requiredModules: ['BaseModule'] }); + Module.define('DependentModule2', { requiredModules: ['BaseModule'] }); - const MyApp = Space.Application.define('MyApp', { + const MyApp = Application.define('MyApp', { requiredModules: ['DependentModule1', 'DependentModule2'] }); diff --git a/tests/unit/application.unit.coffee b/tests/unit/application.unit.coffee index df69d1a..3f2498f 100644 --- a/tests/unit/application.unit.coffee +++ b/tests/unit/application.unit.coffee @@ -1,52 +1,55 @@ +import Application from '../../source/application.coffee'; +import Module from '../../source/module.coffee'; +import {Injector} from '../../source/injector.coffee'; -describe 'Space.Application', -> +describe 'Application', -> beforeEach -> # Reset published space modules - Space.Module.published = {} + Module.published = {} - it 'extends Space.Module', -> - expect(Space.Application).to.extend Space.Module + it 'extends Module', -> + expect(Application).to.extend Module describe 'construction', -> it 'initializes modules map as empty object', -> - expect(new Space.Application().modules).to.eql {} + expect(new Application().modules).to.eql {} it 'creates a new injector instance if none was given', -> - expect(new Space.Application().injector).to.be.instanceof Space.Injector + expect(new Application().injector).to.be.instanceof Injector it 'uses the provided injector when given', -> - injector = new Space.Injector() - application = new Space.Application injector: injector + injector = new Injector() + application = new Application injector: injector expect(application.injector).to.equal injector it 'can also be created via static create method', -> - injector = new Space.Injector() - application = Space.Application.create injector: injector + injector = new Injector() + application = Application.create injector: injector expect(application.injector).to.equal injector - expect(Space.Application.create().injector).to.be.instanceof Space.Injector + expect(Application.create().injector).to.be.instanceof Injector it 'maps injector instance with itself', -> - injector = new Space.Injector() + injector = new Injector() injectionMapping = to: sinon.spy() toInstancesOf: sinon.spy() injector.map = sinon.stub().returns injectionMapping - application = new Space.Application injector: injector + application = new Application injector: injector expect(injector.map).to.have.been.calledWithExactly 'Injector' expect(injectionMapping.to).to.have.been.calledWithExactly injector it 'initializes the application', -> - initializeSpy = sinon.spy Space.Application.prototype, 'initialize' - application = new Space.Application() + initializeSpy = sinon.spy Application.prototype, 'initialize' + application = new Application() expect(initializeSpy).to.have.been.calledOnce initializeSpy.restore() it 'can be passed a configuration', -> - @application = new Space.Application({ + @application = new Application({ configuration: { environment: 'testing' } @@ -54,7 +57,7 @@ describe 'Space.Application', -> expect(@application.configuration.environment).to.equal('testing') it 'merges configurations of all modules and user options', -> - class GrandchildModule extends Space.Module + class GrandchildModule extends Module @publish this, 'GrandchildModule' configuration: { subModuleValue: 'grandChild' @@ -64,7 +67,7 @@ describe 'Space.Application', -> } } - class ChildModule extends Space.Module + class ChildModule extends Module @publish this, 'ChildModule' requiredModules: ['GrandchildModule'] configuration: { @@ -74,7 +77,7 @@ describe 'Space.Application', -> toKeep: 'childKeepMe' } } - class TestApp extends Space.Application + class TestApp extends Application requiredModules: ['ChildModule'] configuration: { toChange: 'appChangeMe' diff --git a/tests/unit/error.tests.js b/tests/unit/error.tests.js index cd1bca2..1e2a1c6 100644 --- a/tests/unit/error.tests.js +++ b/tests/unit/error.tests.js @@ -1,6 +1,8 @@ -describe("Space.Error", function() { +import SpaceError from '../../source/error.js'; - let MyError = Space.Error.extend('MyError', { +describe("SpaceError", function() { + + let MyError = SpaceError.extend('MyError', { message: 'The default message for this error' }); @@ -53,7 +55,7 @@ describe("Space.Error", function() { onConstruction: sinon.spy(), onDependenciesReady: sinon.spy() }; - let MyMixinError = Space.Error.extend('MyMixinError', { mixin: MyMixin }); + let MyMixinError = SpaceError.extend('MyMixinError', { mixin: MyMixin }); let param = 'test'; let error = new MyMixinError(param); expect(MyMixin.onConstruction).to.have.been.calledOn(error); diff --git a/tests/unit/helpers.unit.coffee b/tests/unit/helpers.unit.coffee index ded7bf4..1403215 100644 --- a/tests/unit/helpers.unit.coffee +++ b/tests/unit/helpers.unit.coffee @@ -1,3 +1,4 @@ +import Space from '../../source/namespace.coffee'; global = this diff --git a/tests/unit/injector.unit.coffee b/tests/unit/injector.unit.coffee index fba4adb..652f675 100644 --- a/tests/unit/injector.unit.coffee +++ b/tests/unit/injector.unit.coffee @@ -1,8 +1,9 @@ +import {Injector} from '../../source/injector.coffee'; +import SpaceObject from '../../source/object.coffee'; -Injector = Space.Injector global = this -describe 'Space.Injector', -> +describe 'Injector', -> beforeEach -> @injector = new Injector() @@ -39,7 +40,7 @@ describe 'Space.Injector', -> expect(@injector.create 'test').to.equal 'test' it 'uses the toString method if its not a string id', -> - class TestClass extends Space.Object + class TestClass extends SpaceObject @toString: -> 'TestClass' @injector.map(TestClass).asSingleton() @@ -111,7 +112,7 @@ describe 'Space.Injector', -> it 'injects static values', -> value = {} @injector.map('test').to value - instance = Space.Object.create dependencies: value: 'test' + instance = SpaceObject.create dependencies: value: 'test' @injector.injectInto instance expect(instance.value).to.equal value @@ -126,7 +127,7 @@ describe 'Space.Injector', -> expect(first.value).to.equal 'value' it 'handles inherited dependencies', -> - Base = Space.Object.extend dependencies: base: 'base' + Base = SpaceObject.extend dependencies: base: 'base' Extended = Base.extend dependencies: extended: 'extended' @injector.map('base').to 'base' @injector.map('extended').to 'extended' @@ -137,7 +138,7 @@ describe 'Space.Injector', -> expect(instance.extended).to.equal 'extended' it 'never overrides existing properties', -> - instance = Space.Object.create + instance = SpaceObject.create dependencies: test: 'test' test: 'value' @@ -150,7 +151,7 @@ describe 'Space.Injector', -> it 'tells the instance that they are ready', -> value = 'test' - instance = Space.Object.create + instance = SpaceObject.create dependencies: value: 'value' onDependenciesReady: sinon.spy() @@ -162,7 +163,7 @@ describe 'Space.Injector', -> it 'tells every single instance exactly once', -> readySpy = sinon.spy() - class TestClass extends Space.Object + class TestClass extends SpaceObject dependencies: value: 'test' onDependenciesReady: readySpy @@ -191,7 +192,7 @@ describe 'Space.Injector', -> expect(@injector.get('second')).to.equal value it 'supports global namespace lookup', -> - global.Space.__test__ = TestClass: Space.Object.extend() + global.Space.__test__ = TestClass: SpaceObject.extend() path = 'Space.__test__.TestClass' @injector.map(path).asStaticValue() @@ -240,7 +241,7 @@ describe 'Space.Injector', -> expect(first).to.equal second it 'looks up the value on global namespace if only a path is given', -> - global.Space.__test__ = TestClass: Space.Object.extend() + global.Space.__test__ = TestClass: SpaceObject.extend() @injector.map('Space.__test__.TestClass').asSingleton() first = @injector.get('Space.__test__.TestClass') @@ -258,7 +259,7 @@ describe 'Space.Injector', -> loremIpsum = 'lorem ipsum' - @injector.addProvider 'toLoremIpsum', Space.Object.extend + @injector.addProvider 'toLoremIpsum', SpaceObject.extend Constructor: -> @provide = -> loremIpsum @injector.map('test').toLoremIpsum() diff --git a/tests/unit/injector_annotations.unit.js b/tests/unit/injector_annotations.unit.js index 9772cdb..3cedac6 100644 --- a/tests/unit/injector_annotations.unit.js +++ b/tests/unit/injector_annotations.unit.js @@ -1,4 +1,5 @@ -describe('Space.Injector annotations', function() { + +describe('Injector annotations', function() { describe('Dependency annotation', function() { diff --git a/tests/unit/module.unit.coffee b/tests/unit/module.unit.coffee index ea64987..f31a82f 100644 --- a/tests/unit/module.unit.coffee +++ b/tests/unit/module.unit.coffee @@ -1,63 +1,66 @@ +import SpaceObject from '../../source/object.coffee'; +import Module from '../../source/module.coffee'; +import {Injector} from '../../source/injector.coffee'; -describe 'Space.Module', -> +describe 'Module', -> beforeEach -> # Reset published space modules - Space.Module.published = {} + Module.published = {} - it 'extends space object', -> expect(Space.Module).to.extend Space.Object + it 'extends space object', -> expect(Module).to.extend SpaceObject describe '@publish', -> it 'adds given module to the static collection of published modules', -> - module = Space.Module.define 'test' - expect(Space.Module.published['test']).to.equal module + module = Module.define 'test' + expect(Module.published['test']).to.equal module it 'throws an error if two modules try to publish under same name', -> publishTwoModulesWithSameName = -> - Space.Module.define 'test' - Space.Module.define 'test' + Module.define 'test' + Module.define 'test' expect(publishTwoModulesWithSameName).to.throw Error describe '@require', -> it 'returns published module for given identifier', -> - module = Space.Module.define 'test' - requiredModule = Space.Module.require 'test' + module = Module.define 'test' + requiredModule = Module.require 'test' expect(requiredModule).to.equal module it 'throws and error if no module was registered for given identifier', -> - requireUnkownModule = -> Space.Module.require 'unknown module' + requireUnkownModule = -> Module.require 'unknown module' expect(requireUnkownModule).to.throw Error describe 'constructor', -> it 'sets required modules to empty array if none defined', -> - module = new Space.Module() + module = new Module() expect(module.requiredModules).to.be.instanceof Array expect(module.requiredModules).to.be.empty it 'leaves the defined required modules intact', -> testArray = [] - module = Space.Module.create requiredModules: testArray + module = Module.create requiredModules: testArray expect(module.requiredModules).to.equal testArray it 'sets the correct state', -> - module = new Space.Module() + module = new Module() expect(module.is 'constructed').to.be.true -describe 'Space.Module - #initialize', -> +describe 'Module - #initialize', -> beforeEach -> # Reset published space modules - Space.Module.published = {} - @injector = new Space.Injector() + Module.published = {} + @injector = new Injector() sinon.spy @injector, 'injectInto' - @module = new Space.Module() + @module = new Module() # faked required modules to spy on - @SubModule1 = Space.Module.define 'SubModule1' - @SubModule2 = Space.Module.define 'SubModule2' + @SubModule1 = Module.define 'SubModule1' + @SubModule2 = Module.define 'SubModule2' @app = modules: {} it 'asks the injector to inject dependencies into the module', -> @@ -99,10 +102,10 @@ describe 'Space.Module - #initialize', -> @module.initialize @app, @injector expect(@module.onInitialize).to.have.been.calledOnce -describe 'Space.Module - #start', -> +describe 'Module - #start', -> beforeEach -> - @module = new Space.Module() + @module = new Module() @module.log = {debug: sinon.spy()} @module.start() @module._runLifeCycleAction = sinon.spy() @@ -114,10 +117,10 @@ describe 'Space.Module - #start', -> @module.start() expect(@module._runLifeCycleAction).not.to.have.been.called -describe 'Space.Module - #stop', -> +describe 'Module - #stop', -> beforeEach -> - @module = new Space.Module() + @module = new Module() @module.log = {debug: sinon.spy()} @module.start() @module.stop() @@ -130,10 +133,10 @@ describe 'Space.Module - #stop', -> @module.stop() expect(@module._runLifeCycleAction).not.to.have.been.called -describe 'Space.Module - #reset', -> +describe 'Module - #reset', -> beforeEach -> - @module = new Space.Module() + @module = new Module() @module.log = {debug: sinon.spy()} @module._runLifeCycleAction = sinon.spy() @@ -144,12 +147,12 @@ describe 'Space.Module - #reset', -> process.env.NODE_ENV = nodeEnvBackup expect(@module._runLifeCycleAction).not.to.have.been.called -describe "Space.Module - wrappable lifecycle hooks", -> +describe "Module - wrappable lifecycle hooks", -> it "allows mixins to hook into the module lifecycle", -> moduleOnInitializeSpy = sinon.spy() mixinOnInitializeSpy = sinon.spy() - MyModule = Space.Module.extend { + MyModule = Module.extend { onInitialize: moduleOnInitializeSpy } MyModule.mixin { @@ -159,7 +162,7 @@ describe "Space.Module - wrappable lifecycle hooks", -> mixinOnInitializeSpy.call(this) } module = new MyModule() - module.initialize(module, new Space.Injector()) + module.initialize(module, new Injector()) expect(moduleOnInitializeSpy).to.have.been.calledOnce expect(mixinOnInitializeSpy).to.have.been.calledOnce diff --git a/tests/unit/object.unit.coffee b/tests/unit/object.unit.coffee index 97211e9..f649165 100644 --- a/tests/unit/object.unit.coffee +++ b/tests/unit/object.unit.coffee @@ -1,18 +1,19 @@ +import SpaceObject from '../../source/object.coffee'; -describe 'Space.Object', -> +describe 'SpaceObject', -> beforeEach -> @namespace = {} describe 'extending', -> it 'creates and returns a subclass', -> - Space.Object.extend(@namespace, 'MyClass') - expect(@namespace.MyClass).to.extend Space.Object + SpaceObject.extend(@namespace, 'MyClass') + expect(@namespace.MyClass).to.extend SpaceObject it 'applies the arguments to the super constructor', -> [first, second, third] = ['first', 2, {}] spy = sinon.spy() - Space.Object.extend @namespace, 'Base', { + SpaceObject.extend @namespace, 'Base', { Constructor: -> spy.apply this, arguments } @namespace.Base.extend(@namespace, 'Extended') @@ -21,7 +22,7 @@ describe 'Space.Object', -> expect(spy).to.have.been.calledOn instance it 'allows to extend the prototype', -> - First = Space.Object.extend first: 1, get: (property) -> @[property] + First = SpaceObject.extend first: 1, get: (property) -> @[property] Second = First.extend second: 2, get: -> First::get.apply this, arguments class Third extends Second get: (property) -> super property @@ -33,57 +34,57 @@ describe 'Space.Object', -> it "registers the class for internal lookup", -> Space.namespace('My.custom') - FirstClass = Space.Object.extend('My.custom.FirstClass', {}) - SecondClass = Space.Object.extend('My.custom.SecondClass', {}) + FirstClass = SpaceObject.extend('My.custom.FirstClass', {}) + SecondClass = SpaceObject.extend('My.custom.SecondClass', {}) expect(Space.resolvePath 'My.custom.FirstClass').to.equal(FirstClass) expect(Space.resolvePath 'My.custom.SecondClass').to.equal(SecondClass) it "assigns the class path", -> className = 'My.custom.Class' - MyClass = Space.Object.extend(className) + MyClass = SpaceObject.extend(className) expect(MyClass.toString()).to.equal(className) expect(new MyClass().toString()).to.equal(className) it "exposes the class on the global scope if possible", -> my = {} my.namespace = Space.namespace('my.namespace') - MyClass = Space.Object.extend('my.namespace.MyClass') + MyClass = SpaceObject.extend('my.namespace.MyClass') expect(my.namespace.MyClass).to.equal(MyClass) it "works correctly without nested namespaces", -> - MyClass = Space.Object.extend('MyClass') + MyClass = SpaceObject.extend('MyClass') expect(Space.resolvePath 'MyClass').to.equal(MyClass) describe "working with static class properties", -> it 'allows you to define static class properties', -> myStatics = {} - MyClass = Space.Object.extend statics: { myStatics: myStatics } + MyClass = SpaceObject.extend statics: { myStatics: myStatics } expect(MyClass.myStatics).to.equal(myStatics) it 'provides an api for defining a callback while extending', -> onExtendingSpy = sinon.spy() - MyClass = Space.Object.extend onExtending: onExtendingSpy + MyClass = SpaceObject.extend onExtending: onExtendingSpy expect(onExtendingSpy).to.have.been.calledOn(MyClass) describe 'creating instances', -> it 'creates a new instance of given class', -> - expect(Space.Object.create()).to.be.instanceof Space.Object + expect(SpaceObject.create()).to.be.instanceof SpaceObject it 'allows to initialize the instance with given properties', -> - instance = Space.Object.create first: 1, get: (property) -> @[property] + instance = SpaceObject.create first: 1, get: (property) -> @[property] expect(instance.get 'first').to.equal 1 it 'forwards any number of arguments to the constructor', -> - Base = Space.Object.extend Constructor: (@first, @second) -> + Base = SpaceObject.extend Constructor: (@first, @second) -> instance = Base.create 1, 2 expect(instance.first).to.equal 1 expect(instance.second).to.equal 2 describe "inheritance helpers", -> - Base = Space.Object.extend { + Base = SpaceObject.extend { statics: { prop: 'static', method: -> } prop: 'prototype' method: -> @@ -100,7 +101,7 @@ describe 'Space.Object', -> expect(Sub.superClass()).to.equal(Base) it "returns undefined if there is no super class", -> - expect(Space.Object.superClass()).to.equal(undefined) + expect(SpaceObject.superClass()).to.equal(undefined) it "can return a static prop or method of the super class", -> expect(Sub.superClass('prop')).to.equal(Base.prop) @@ -127,26 +128,26 @@ describe 'Space.Object', -> it 'adds methods to the prototype', -> testMixin = test: -> - TestClass = Space.Object.extend() + TestClass = SpaceObject.extend() TestClass.mixin testMixin expect(TestClass::test).to.equal testMixin.test it 'overrides existing methods of the prototype', -> testMixin = test: -> - TestClass = Space.Object.extend test: -> + TestClass = SpaceObject.extend test: -> TestClass.mixin testMixin expect(TestClass::test).to.equal testMixin.test it 'merges object properties', -> testMixin = dependencies: second: 'second' - TestClass = Space.Object.extend dependencies: first: 'first' + TestClass = SpaceObject.extend dependencies: first: 'first' TestClass.mixin testMixin expect(TestClass::dependencies.first).to.equal 'first' expect(TestClass::dependencies.second).to.equal 'second' it "does not modify other mixins when merging properties", -> FirstMixin = dependencies: firstMixin: 'onExtending' - FirstClass = Space.Object.extend { + FirstClass = SpaceObject.extend { mixin: [FirstMixin] dependencies: first: 'first' } @@ -159,18 +160,18 @@ describe 'Space.Object', -> it "can provide a hook that is called when the mixin is applied", -> myMixin = onMixinApplied: sinon.spy() - TestClass = Space.Object.extend() + TestClass = SpaceObject.extend() TestClass.mixin myMixin expect(myMixin.onMixinApplied).to.have.been.calledOnce it 'can be defined as prototype property when extending classes', -> myMixin = { onMixinApplied: sinon.spy() } - MyClass = Space.Object.extend mixin: [myMixin] + MyClass = SpaceObject.extend mixin: [myMixin] expect(myMixin.onMixinApplied).to.have.been.calledOn(MyClass) it 'can be used to mixin static properties on to the class', -> myMixin = statics: { myMethod: sinon.spy() } - MyClass = Space.Object.extend mixin: [myMixin] + MyClass = SpaceObject.extend mixin: [myMixin] MyClass.myMethod() expect(myMixin.statics.myMethod).to.have.been.calledOn(MyClass) @@ -178,7 +179,7 @@ describe 'Space.Object', -> FirstMixin = {} SecondMixin = {} ThirdMixin = {} - MyClass = Space.Object.extend({ mixin: FirstMixin }) + MyClass = SpaceObject.extend({ mixin: FirstMixin }) MyClass.mixin(SecondMixin) instance = new MyClass() # Static checks @@ -195,7 +196,7 @@ describe 'Space.Object', -> it "does not apply mixins to super classes", -> firstMixin = {} secondMixin = {} - SuperClass = Space.Object.extend mixin: firstMixin + SuperClass = SpaceObject.extend mixin: firstMixin SubClass = SuperClass.extend mixin: secondMixin expect(SuperClass.hasMixin(firstMixin)).to.be.true expect(SuperClass.hasMixin(secondMixin)).to.be.false @@ -205,7 +206,7 @@ describe 'Space.Object', -> it "inherits mixins to children when added to base class later on", -> LateMixin = { statics: { test: 'property' } } # Base class with a mixin - BaseClass = Space.Object.extend() + BaseClass = SpaceObject.extend() # Sublcass with its own mixin SubClass = BaseClass.extend() # Later we extend base class @@ -218,7 +219,7 @@ describe 'Space.Object', -> it "can provide a hook that is called when dependencies of host class are ready", -> myMixin = onDependenciesReady: sinon.spy() - TestClass = Space.Object.extend() + TestClass = SpaceObject.extend() TestClass.mixin myMixin new TestClass().onDependenciesReady() expect(myMixin.onDependenciesReady).to.have.been.calledOnce @@ -226,7 +227,7 @@ describe 'Space.Object', -> it "inherits the onDependenciesReady hooks to sub classes", -> firstMixin = onDependenciesReady: sinon.spy() secondMixin = onDependenciesReady: sinon.spy() - SuperClass = Space.Object.extend() + SuperClass = SpaceObject.extend() SuperClass.mixin firstMixin SubClass = SuperClass.extend() SubClass.mixin secondMixin @@ -236,7 +237,7 @@ describe 'Space.Object', -> it "calls inherited mixin hooks only once per chain", -> myMixin = onDependenciesReady: sinon.spy() - SuperClass = Space.Object.extend() + SuperClass = SpaceObject.extend() SuperClass.mixin myMixin SubClass = SuperClass.extend() new SubClass().onDependenciesReady() @@ -246,7 +247,7 @@ describe 'Space.Object', -> it "can provide a hook that is called on construction of host class", -> myMixin = onConstruction: sinon.spy() - TestClass = Space.Object.extend() + TestClass = SpaceObject.extend() TestClass.mixin myMixin first = {} second = {} diff --git a/tests/unit/struct.unit.coffee b/tests/unit/struct.unit.coffee index fb7b4f0..d66435c 100644 --- a/tests/unit/struct.unit.coffee +++ b/tests/unit/struct.unit.coffee @@ -1,8 +1,9 @@ import {MatchError, Integer} from 'simplecheck'; +import Struct from '../../source/struct.coffee'; +import SpaceObject from '../../source/object.coffee'; -describe 'Space.Struct', -> - - class MyTestStruct extends Space.Struct +describe 'Struct', -> + class MyTestStruct extends Struct @type 'MyTestStruct' fields: -> name: String, age: Integer @@ -13,13 +14,13 @@ describe 'Space.Struct', -> fields.extra = Integer return fields - it "is a Space.Object", -> - expect(Space.Struct).to.extend(Space.Object) + it "is a SpaceObject", -> + expect(Struct).to.extend(SpaceObject) it "calls the super constructor", -> - constructorSpy = sinon.spy(Space.Object.prototype, 'constructor') + constructorSpy = sinon.spy(SpaceObject.prototype, 'constructor') data = {} - struct = new Space.Struct(data) + struct = new Struct(data) expect(constructorSpy).to.have.been.calledWithExactly(data) expect(constructorSpy).to.have.been.calledOn(struct) constructorSpy.restore() @@ -54,7 +55,7 @@ describe 'Space.Struct', -> # TODO: remove when breaking change is made for next major version: it 'stays backward compatible with static fields api', -> - class StaticFieldsStruct extends Space.Struct + class StaticFieldsStruct extends Struct @fields: { name: String, age: Integer } properties = name: 'Dominik', age: 26 From 39532d7555e47083585fdd644b252cdc61e3e170 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 12:19:35 +0100 Subject: [PATCH 027/111] Standardizes file naming convention --- .../{injector_annotations.coffee => injector-annotations.coffee} | 0 ...score_deep_extend_mixin.js => underscore-deep-extend-mixin.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename source/{injector_annotations.coffee => injector-annotations.coffee} (100%) rename source/lib/{underscore_deep_extend_mixin.js => underscore-deep-extend-mixin.js} (100%) diff --git a/source/injector_annotations.coffee b/source/injector-annotations.coffee similarity index 100% rename from source/injector_annotations.coffee rename to source/injector-annotations.coffee diff --git a/source/lib/underscore_deep_extend_mixin.js b/source/lib/underscore-deep-extend-mixin.js similarity index 100% rename from source/lib/underscore_deep_extend_mixin.js rename to source/lib/underscore-deep-extend-mixin.js From 3fe3df1e960038b03cd0aab8ff32066d809e99cd Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 12:36:55 +0100 Subject: [PATCH 028/111] Sets npm dependencies versions to minor --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4a7a7d5..2321e87 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,10 @@ "version": "4.1.4", "description": "Modular Application Architecture", "dependencies": { - "getenv": "0.7.x", - "underscore": "1.8.3", - "simplecheck": "git://github.com/meteor-space/base.git" + "getenv": "0.7.*", + "underscore": "1.8.*", + "simplecheck": "git://github.com/qejk/simplecheck.git", + "lodash": "4.17.*" }, "devDependencies": {}, "scripts": {}, From e3af6bcd8b31c83d946132f476794c3c8485b862 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 12:37:54 +0100 Subject: [PATCH 029/111] Converts injector-annotations --- source/injector-annotations.coffee | 19 ------------------- source/injector-annotations.js | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 19 deletions(-) delete mode 100644 source/injector-annotations.coffee create mode 100644 source/injector-annotations.js diff --git a/source/injector-annotations.coffee b/source/injector-annotations.coffee deleted file mode 100644 index 09b5c87..0000000 --- a/source/injector-annotations.coffee +++ /dev/null @@ -1,19 +0,0 @@ -import _ from 'underscore'; - -@Space.Dependency = (propertyName, dependencyId) -> - if (typeof dependencyId == 'undefined') - dependencyId = propertyName - (target) -> - if target.prototype.dependencies and not target.prototype.hasOwnProperty('Dependencies') - target.prototype.dependencies = _.clone target.prototype.dependencies - target.prototype.dependencies ?= {} - target.prototype.dependencies[propertyName] = dependencyId - return target - -@Space.RequireModule = (moduleId) -> - (target) -> - if target.prototype.requiredModules and not target.prototype.hasOwnProperty('RequiredModules') - target.prototype.requiredModules = _.clone target.prototype.requiredModules - target.prototype.requiredModules ?= [] - target.prototype.requiredModules.push moduleId - return target diff --git a/source/injector-annotations.js b/source/injector-annotations.js new file mode 100644 index 0000000..7cc9834 --- /dev/null +++ b/source/injector-annotations.js @@ -0,0 +1,26 @@ +import _ from 'underscore'; +import {isNil} from 'lodash'; + +this.Space.Dependency = function(propertyName, dependencyId) { + return function(target) { + const proto = target.prototype; + if (proto.dependencies && !proto.hasOwnProperty('Dependencies')) { + proto.dependencies = _.clone(proto.dependencies); + } + if (isNil(proto.dependencies)) {proto.dependencies = {};} + proto.dependencies[propertyName] = dependencyId || propertyName; + return target; + }; +}; + +this.Space.RequireModule = function(moduleId) { + return function(target) { + const proto = target.prototype; + if (proto.requiredModules && !proto.hasOwnProperty('RequiredModules')) { + proto.requiredModules = _.clone(proto.requiredModules); + } + if (isNil(proto.requiredModules)) {proto.requiredModules = [];} + proto.requiredModules.push(moduleId); + return target; + }; +}; From 35ad2707a3d736eda178125463d279ff00a0d8d6 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 12:46:19 +0100 Subject: [PATCH 030/111] Converts namespace --- source/namespace.coffee | 11 ----------- source/namespace.js | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) delete mode 100644 source/namespace.coffee create mode 100644 source/namespace.js diff --git a/source/namespace.coffee b/source/namespace.coffee deleted file mode 100644 index e6c540d..0000000 --- a/source/namespace.coffee +++ /dev/null @@ -1,11 +0,0 @@ -class Namespace - constructor: (@_path) -> - getPath: -> this._path - toString: -> @_path - -# Define global namespace for the space framework -@Space = new Namespace 'Space' -@Space.Namespace = Namespace -@Space.registry = {} - -export default Space; \ No newline at end of file diff --git a/source/namespace.js b/source/namespace.js new file mode 100644 index 0000000..743247e --- /dev/null +++ b/source/namespace.js @@ -0,0 +1,18 @@ +class Namespace { + constructor(path) { + this._path = path; + } + getPath() { + return this._path; + } + toString() { + return this._path; + } +} + +// Define global namespace for the space framework +this.Space = new Namespace('Space'); +this.Space.Namespace = Namespace; +this.Space.registry = {}; + +export default Space; From 84aea8033f9d50de9cb92ab205c18720f6760732 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 12:50:17 +0100 Subject: [PATCH 031/111] Converts helpers --- source/helpers.coffee | 25 ------------------------- source/helpers.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 25 deletions(-) delete mode 100644 source/helpers.coffee create mode 100644 source/helpers.js diff --git a/source/helpers.coffee b/source/helpers.coffee deleted file mode 100644 index 8be221b..0000000 --- a/source/helpers.coffee +++ /dev/null @@ -1,25 +0,0 @@ - -global = this - -# Resolves a (possibly nested) path to a global object -# Returns the object or null (if not found) -Space.resolvePath = (path) -> - if !path? then throw new Error "Cannot resolve invalid path <#{path}>" - if path == '' then return global - - # If there is a direct reference just return it - if Space.registry[path]? then return Space.registry[path] - if Space.Module?.published[path]? then return Space.Module.published[path] - parts = path.split '.' - result = global # Start with global namespace - for key in parts # Move down the object chain - result = result?[key] ? null - # Take published space modules into account - # to solve the Meteor package scoping problem - if !result? then throw new Error "Could not resolve path '#{path}'" - return result - -Space.namespace = (id) -> Space.registry[id] = new Space.Namespace(id) - -Space.capitalizeString = (string) -> - string.charAt(0).toUpperCase() + string.slice(1) diff --git a/source/helpers.js b/source/helpers.js new file mode 100644 index 0000000..17a3659 --- /dev/null +++ b/source/helpers.js @@ -0,0 +1,38 @@ +import {isNil, get, capitalize} from 'lodash'; +import Space from './namespace.js'; + +const global = this; + +// Resolves a (possibly nested) path to a global object +// Returns the object or null (if not found) +Space.resolvePath = function(path) { + if (isNil(path)) {throw new Error(`Cannot resolve invalid path <${path}>`);} + if (path === '') {return global;} + + // If there is a direct reference just return it + if (Space.registry && !isNil(Space.registry[path])) { + return Space.registry[path]; + } + if (get(Space, `Module.published.${path}`)) { + return Space.Module.published[path]; + } + parts = path.split('.'); + result = global; // Start with global namespace + for (let key of parts) { // Move down the object chain + result = get(result, key); + // Take published space modules into account + // to solve the Meteor package scoping problem + if (isNil(result)) { + throw new Error("Could not resolve path '" + path + "'"); + } + } + return result; +}; + +Space.namespace = function(id) { + Space.registry[id] = new Space.Namespace(id); + return Space.registry[id]; +}; + +// @backward {space:base} <= 4.1.3 +Space.capitalizeString = capitalize; From 4e45a4808cf1a8f544d94ccc197a2db85ba907e8 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 13:17:35 +0100 Subject: [PATCH 032/111] Imports Struct module on Space error and removes it from global Space --- source/error.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/error.js b/source/error.js index d432e1e..51f960b 100644 --- a/source/error.js +++ b/source/error.js @@ -1,10 +1,11 @@ import _ from 'underscore'; import {optional, Integer} from 'simplecheck'; +import Struct from './struct.js'; let IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; -Space.Error = function(params) { +SpaceError = function(params) { this._invokeConstructionCallbacks.apply(this, arguments); let data = null; if (_.isString(params)) { @@ -14,20 +15,20 @@ Space.Error = function(params) { } else { data = {}; } - Space.Struct.call(this, this.extractErrorProperties(data)); + Struct.call(this, this.extractErrorProperties(data)); return this; }; -Space.Error.prototype = new IntermediateInheritor(); +SpaceError.prototype = new IntermediateInheritor(); _.extend( - Space.Error.prototype, // target - Space.Struct.prototype, + SpaceError.prototype, // target + Struct.prototype, _.omit(Space.Object.prototype, 'toString'), { message: '', fields() { - let fields = Space.Struct.prototype.fields.call(this); + let fields = Struct.prototype.fields.call(this); _.extend(fields, { name: String, message: String, @@ -47,8 +48,8 @@ _.extend( } ); -_.extend(Space.Error, _.omit(Space.Object, 'toString'), { +_.extend(SpaceError, _.omit(Space.Object, 'toString'), { __keepToStringMethod__: true // Do not override #toString method }); -export default Space.Error; +export default SpaceError; From bad61c9758a885f829389baaa2fd351e440c500c Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 13:42:28 +0100 Subject: [PATCH 033/111] Converts Injector, imports SpaceError and removes Injector from Space global --- source/injector.coffee | 211 -------------------------- source/injector.js | 326 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 211 deletions(-) delete mode 100644 source/injector.coffee create mode 100644 source/injector.js diff --git a/source/injector.coffee b/source/injector.coffee deleted file mode 100644 index 2ec284b..0000000 --- a/source/injector.coffee +++ /dev/null @@ -1,211 +0,0 @@ -import _ from 'underscore'; - -InjectionError = Space.Error.extend('Space.InjectionError') - -Injector = class Space.Injector - - ERRORS: { - cannotMapUndefinedId: -> 'Cannot map or .' - mappingExists: (id) -> "<#{id}> would be overwritten. Use for that." - noMappingFound: (id) -> "no mapping found for <#{id}>" - cannotGetValueForUndefined: -> "Cannot get injection mapping for ." - } - - constructor: (providers) -> - @_mappings = {} - @_providers = providers ? Injector.DEFAULT_PROVIDERS - - toString: -> 'Instance ' - - map: (id, override) -> - if not id? - throw new Space.InjectionError(@ERRORS.cannotMapUndefinedId()) - mapping = @_mappings[id] - # Avoid accidential override of existing mapping - if mapping? and !override - throw new Space.InjectionError(@ERRORS.mappingExists(id)) - else if mapping? and override - mapping.markForOverride() - return mapping - else - @_mappings[id] = new Mapping id, @_providers - return @_mappings[id] - - override: (id) -> @map id, true - - remove: (id) -> delete @_mappings[id] - - get: (id, dependentObject=null) -> - if !id? - throw new Space.InjectionError(@ERRORS.cannotGetValueForUndefined()) - if not @_mappings[id]? - throw new Space.InjectionError(@ERRORS.noMappingFound(id)) - dependency = @_mappings[id].provide(dependentObject) - @injectInto dependency - return dependency - - create: (id) -> @get id - - injectInto: (value) -> - unless _.isObject(value) and !value.__dependenciesInjected__ then return - if Object.defineProperty? - # Flag this object as injected - Object.defineProperty value, '__dependenciesInjected__', - enumerable: false - configurable: false - writable: false - value: true - else - # support old engines without Object.defineProperty - value.__dependenciesInjected__ = true - # Get flat map of dependencies (possibly inherited) - dependencies = @_mapDependencies value - # Inject into dependencies to create the object graph - for key, id of dependencies - try - value[key] ?= @get(id, value) - catch error - error.message += " for {#{key}: '#{id}'} in <#{value}>. Did you forget - to map it in your application?" - throw error - # Notify when dependencies are ready - if value.onDependenciesReady? then value.onDependenciesReady() - - addProvider: (name, provider) -> @_providers[name] = provider - - getMappingFor: (id) -> @_mappings[id] - - getIdForValue: (value) -> - for id, mapping of @_mappings - return id if mapping.getProvider().getValue() is value - - release: (dependent) -> - for id, mapping of @_mappings - mapping.release(dependent) if mapping.hasDependent(dependent) - - _mapDependencies: (value, deps={}) -> - Class = value.constructor ? null - SuperClass = Class.__super__ ? null - # Recurse down the prototype chain - if SuperClass? then @_mapDependencies SuperClass.constructor::, deps - # Add dependencies of current value - deps[key] = id for key, id of value.dependencies - return deps - - _resolveValue: (path) -> Space.resolvePath path - -# ========= PRIVATE CLASSES ========== # - -class Mapping - - _id: null - _provider: null - _dependents: null - _overrideInDependents: false - - constructor: (@_id, providers) -> - @_dependents = [] - @[key] = @_setup(provider) for key, provider of providers - - toString: -> 'Instance ' - - provide: (dependent) -> - # Register depented objects for this mapping so that their - # dependencies can overwritten later on. - @_dependents.push(dependent)if dependent? and not @hasDependent(dependent) - @_provider.provide() - - markForOverride: -> @_overrideInDependents = true - - hasDependent: (dependent) -> @getIndexOfDependee(dependent) > -1 - - getIndexOfDependee: (dependent) -> @_dependents.indexOf(dependent) - - release: (dependent) -> @_dependents.splice(@getIndexOfDependee(dependent), 1) - - getId: -> @_id - - getProvider: -> @_provider - - _setup: (provider) -> - return (value) => # We are inside an API call like injector.map('this').to('that') - # Set the provider of this mapping to what the API user chose - try - @_provider = new provider @_id, value - catch error - error.message += " could not be found! Maybe you forgot to - include a file in package.js?" - throw error - # Override the dependency in all dependent objects if this mapping is flagged - if @_overrideInDependents - # Get the value from the provider - value = @_provider.provide() - # Loop over the dependents - for dependent in @_dependents - # Loop over their dependencies and override the one this mapping - # is managing if it exists (it should) - dependencies = dependent.dependencies ? {} - for key, id of dependencies - if id is @_id - dependent[key] = value - dependent.onDependencyChanged?(key, value) - - # Reset the flag to override dependencies - @_overrideInDependents = false - -# ========= DEFAULT PROVIDERS ======== # - -class Provider - - _id: null - _value: null - - constructor: (@_id, @_value) -> - - getValue: -> @_value - -class ValueProvider extends Provider - - constructor: -> - super - if not @_value? - if (typeof @_id is 'string') - @_value = Space.resolvePath(@_id) - else - @_value = @_id - - toString: -> 'Instance ' - - provide: -> @_value - -class InstanceProvider extends Provider - - toString: -> 'Instance ' - - provide: -> new @_value() - -class SingletonProvider extends Provider - - _singleton: null - - constructor: -> - super - if not @_value? then @_value = @_id - if typeof(@_value) is 'string' then @_value = Space.resolvePath(@_value) - - toString: -> 'Instance ' - - provide: -> - if not @_singleton? then @_singleton = new @_value() - return @_singleton - -Space.Injector.DEFAULT_PROVIDERS = - to: ValueProvider - toStaticValue: ValueProvider - asStaticValue: ValueProvider - toClass: InstanceProvider - toInstancesOf: InstanceProvider - asSingleton: SingletonProvider - toSingleton: SingletonProvider - -export {InjectionError as InjectionError, Injector as Injector}; \ No newline at end of file diff --git a/source/injector.js b/source/injector.js new file mode 100644 index 0000000..88d9728 --- /dev/null +++ b/source/injector.js @@ -0,0 +1,326 @@ +import _ from 'underscore'; +import {isNil} from 'lodash'; +import SpaceError from './error.js'; + +InjectionError = SpaceError.extend('Space.InjectionError'); + +class Injector { + constructor(providers) { + this._mappings = {}; + this._providers = providers || Injector.DEFAULT_PROVIDERS; + } + + toString() { + return 'Instance '; + } + + map(id, override) { + if (isNil(id)) { + throw new InjectionError(this.ERRORS.cannotMapUndefinedId()); + } + const mapping = this._mappings[id]; + // Avoid accidential override of existing mapping + if (!isNil(mapping) && !override) { + throw new InjectionError(this.ERRORS.mappingExists(id)); + } else if (!isNil(mapping) && override) { + mapping.markForOverride(); + return mapping; + } else { + this._mappings[id] = new Mapping(id, this._providers); + return this._mappings[id]; + } + } + + override(id) { + return this.map(id, true); + } + + remove(id) { + delete this._mappings[id]; + } + + get(id, dependentObject = null) { + if (isNil(id)) { + throw new InjectionError(this.ERRORS.cannotGetValueForUndefined()); + } + if (isNil(this._mappings[id])) { + throw new InjectionError(this.ERRORS.noMappingFound(id)); + } + const dependency = this._mappings[id].provide(dependentObject); + this.injectInto(dependency); + return dependency; + } + + create(id) { + return this.get(id); + } + + injectInto(value) { + if (!(_.isObject(value) && !value.__dependenciesInjected__)) {return;} + + if (!isNil(Object.defineProperty)) { + // Flag this object as injected + Object.defineProperty(value, '__dependenciesInjected__', { + enumerable: false, + configurable: false, + writable: false, + value: true + }); + } else { + // Support old engines without Object.defineProperty + value.__dependenciesInjected__ = true; + } + // Get flat map of dependencies (possibly inherited) + const dependencies = this._mapDependencies(value); + // Inject into dependencies to create the object graph + for (let [key, id] of Object.entries(dependencies)) { + try { + if (isNil(value[key])) {value[key] = this.get(id, value);} + } catch (error) { + error.message += ` for {${key}: '${id}'} in <${value}>. Did you forget + to map it in your application?`; + throw error; + } + } + // Notify when dependencies are ready + if (!isNil(value.onDependenciesReady)) { + value.onDependenciesReady(); + } + } + + addProvider(name, provider) { + this._providers[name] = provider; + } + + getMappingFor(id) { + return this._mappings[id]; + } + + getIdForValue(value) { + for (let [id, mapping] of Object.entries(this._mappings)) { + if (mapping.getProvider().getValue() === value) { + return id; + } + } + return null; + } + + release(dependent) { + for (let mapping of Object.values(this._mappings)) { + if (mapping.hasDependent(dependent)) { + mapping.release(dependent); + } + } + } + + _mapDependencies(value, deps = {}) { + Class = value.constructor || null; + SuperClass = Class.__super__ || null; + // Recurse down the prototype chain + if (!isNil(SuperClass)) { + this._mapDependencies(SuperClass.constructor.prototype, deps); + } + if (isNil(value.dependencies)) {return deps;} + // Add dependencies of current value + for (let [key, id] of Object.entries(value.dependencies)) { + deps[key] = id; + } + return deps; + } + + _resolveValue(path) { + return Space.resolvePath(path); + } +} + +Injector.prototype.ERRORS = { + cannotMapUndefinedId() { + return 'Cannot map or .'; + }, + mappingExists(id) { + return `<${id}> would be overwritten. Use for that.`; + }, + noMappingFound(id) { + return `No mapping found for <${id}>`; + }, + cannotGetValueForUndefined() { + return "Cannot get injection mapping for ."; + } +}; + +// ========= PRIVATE CLASSES ========== + +class Mapping { + constructor(id, providers = {}) { + this._id = id; + this._provider = null; + this._dependents = []; + this._overrideInDependents = null; + + for (let [key, provider] of Object.entries(providers)) { + this[key] = this._setup(provider); + } + } + + toString() { + return 'Instance '; + } + + provide(dependent) { + // Register depented objects for this mapping so that their + // dependencies can overwritten later on. + if (!isNil(dependent) && !this.hasDependent(dependent)) { + this._dependents.push(dependent); + } + return this._provider.provide(); + } + + markForOverride() { + this._overrideInDependents = true; + } + + hasDependent(dependent) { + return this.getIndexOfDependee(dependent) > -1; + } + + getIndexOfDependee(dependent) { + return this._dependents.indexOf(dependent); + } + + release(dependent) { + this._dependents.splice(this.getIndexOfDependee(dependent), 1); + } + + getId() { + return this._id; + } + + getProvider() { + return this._provider; + } + + _setup(provider) { + return ((value) => { // We are inside an API call like + // injector.map('this').to('that') + // Set the provider of this mapping to what the API user chose + try { + this._provider = new provider(this._id, value); + } catch (error) { + error.message += ` could not be found! Maybe you forgot to include a file + in package.js?`; + throw error; + } + // Override the dependency in all dependent objects if this mapping is flagged + if (this._overrideInDependents) { + // Get the value from the provider + providersValue = this._provider.provide(); + // Loop over the dependents + for (let dependent of Object.values(this._dependents)) { + // Loop over their dependencies and override the one this mapping + // is managing if it exists (it should) + dependencies = dependent.dependencies || {}; + for (let [key, id] of Object.entries(dependencies)) { + if (id === this._id) { + dependent[key] = providersValue; + if (!isNil(dependent.onDependencyChanged)) { + dependent.onDependencyChanged(key, value); + } + } + } + } + } + // Reset the flag to override dependencies + this._overrideInDependents = false; + }); + } +} + +// ========= DEFAULT PROVIDERS ======== + +class Provider { + constructor(id = null, value = null) { + this._id = id; + this._value = value; + } + + getValue() { + return this._value; + } +} + +class ValueProvider extends Provider { + constructor(id, value) { + super(id, value); + if (isNil(this._value)) { + if (_.isString(this._id)) { + this._value = Space.resolvePath(this._id); + } else { + this._value = this._id; + } + } + } + + toString() { + return 'Instance '; + } + + provide() { + return this._value; + } +} + +class InstanceProvider extends Provider { + + toString() { + return 'Instance '; + } + + provide() { + return new this._value(); + } +} + +class SingletonProvider extends Provider { + constructor(id, value) { + super(id, value); + this.singleton = null; + if (isNil(this._value)) { + this._value = this._id; + } + if (_.isString(this._value)) { + this._value = Space.resolvePath(this._value); + } + } + + toString() { + return 'Instance '; + } + + provide() { + if (isNil(this._singleton)) { + this._singleton = new this._value(); + } + return this._singleton; + } +} + +Injector.DEFAULT_PROVIDERS = { + to: ValueProvider, + toStaticValue: ValueProvider, + asStaticValue: ValueProvider, + toClass: InstanceProvider, + toInstancesOf: InstanceProvider, + asSingleton: SingletonProvider, + toSingleton: SingletonProvider +}; + +Space.Injector = Injector; + +export { + InjectionError as InjectionError, + Injector as Injector, + Provider as Provider, + ValueProvider as ValueProvider, + InstanceProvider as InstanceProvider, + SingletonProvider as SingletonProvider +}; From 27416ab712f867da248304c74231701b991e1295 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 14:15:37 +0100 Subject: [PATCH 034/111] Converts Module and removes it from Space global --- source/module.coffee | 190 ----------------------------- source/module.js | 282 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 190 deletions(-) delete mode 100644 source/module.coffee create mode 100644 source/module.js diff --git a/source/module.coffee b/source/module.coffee deleted file mode 100644 index cc3fd77..0000000 --- a/source/module.coffee +++ /dev/null @@ -1,190 +0,0 @@ -import _ from 'underscore'; -import Logger from './logger.js'; - -class Space.Module extends Space.Object - - ERRORS: { - injectorMissing: 'Instance of Space.Injector needed to initialize module.' - } - - configuration: {} - requiredModules: null - # An array of paths to classes that you want to become - # singletons in your application e.g: ['Space.messaging.EventBus'] - # these are automatically mapped and created on `app.run()` - singletons: [] - injector: null - _state: 'constructed' - - constructor: -> - super - @requiredModules ?= [] - - initialize: (@app, @injector, isSubModule=false) -> - return if not @is('constructed') # only initialize once - if not @injector? then throw new Error @ERRORS.injectorMissing - - @_state = 'configuring' - unless isSubModule - @log = @_setupLogger() - else - @log = @injector.get('log') - @log.debug("#{@constructor.publishedAs}: initialize") - - # Setup basic mappings required by all modules if this the top-level module - unless isSubModule - @injector.map('Injector').to @injector - @_mapSpaceServices() - - # Setup required modules - for moduleId in @requiredModules - # Create a new module instance if not already registered with the app - unless @app.modules[moduleId]? - moduleClass = Space.Module.require(moduleId, this.constructor.name) - @app.modules[moduleId] = new moduleClass() - # Initialize required module - module = @app.modules[moduleId] - module.initialize(@app, @injector, true) - - # Merge in own configuration to give the chance for overwriting. - if isSubModule - _.deepExtend(@app.configuration, @configuration) - @configuration = @app.configuration - else - # The app can override all other modules - _.deepExtend(@configuration, @constructor.prototype.configuration) - - # Provide lifecycle hook before any initialization has been done - @beforeInitialize?() - # Give every module access Npm - if Meteor.isServer then @npm = Npm - # Top-level module - if not isSubModule - @injector.map('configuration').to(@configuration) - @_runOnInitializeHooks() - @_autoMapSingletons() - @_autoCreateSingletons() - @_runAfterInitializeHooks() - - start: -> - if @is('running') then return - @_runLifeCycleAction 'start' - @_state = 'running' - - reset: -> - return if Meteor.isServer and process.env.NODE_ENV is 'production' - return if @_isResetting - restartRequired = @is('running') - @_isResetting = true - if restartRequired then @stop() - @_runLifeCycleAction 'reset' - if restartRequired then @start() - # There is no other way to avoid reset being called multiple times - # if multiple modules require the same sub-module. - Meteor.defer => @_isResetting = false - - stop: -> - if @is('stopped') then return - @_runLifeCycleAction 'stop', => - @_state = 'stopped' - - is: (expectedState) -> expectedState is @_state - - # ========== STATIC MODULE MANAGEMENT ============ # - - @define: (moduleName, prototype={}) -> - prototype.toString = -> moduleName # For better debugging - @publish Space.Module.extend(moduleName, prototype), moduleName - - # All published modules register themselves here - @published = {} - - # Publishes a module into the space environment to make it - # visible and requireable for other modules and the application - @publish: (module, identifier) -> - module.publishedAs = module.name = identifier - if Space.Module.published[identifier]? - throw new Error "Two modules tried to be published as <#{identifier}>" - else - Space.Module.published[identifier] = module - - # Retrieve a module by identifier - @require: (requiredModule, requestingModule) -> - module = Space.Module.published[requiredModule] - if not module? - throw new Error "Could not find module <#{requiredModule}> - required by <#{requestingModule}>" - else - return module - - # Invokes the lifecycle action on all required modules, then on itself, - # calling the instance hooks before, on, and after - _runLifeCycleAction: (action, func) -> - @_invokeActionOnRequiredModules action - @log.debug("#{@constructor.publishedAs}: #{action}") - this["before#{Space.capitalizeString(action)}"]?() - func?() - this["on#{Space.capitalizeString(action)}"]?() - this["after#{Space.capitalizeString(action)}"]?() - - # Provide lifecycle hook after this module was configured and injected - _runOnInitializeHooks: -> - @_invokeActionOnRequiredModules '_runOnInitializeHooks' - # Never run this hook twice - if @is('configuring') - @log.debug("#{@constructor.publishedAs}: onInitialize") - @_state = 'initializing' - # Inject required dependencies into this module - @injector.injectInto this - # Call custom lifecycle hook if existant - @onInitialize?() - - _autoMapSingletons: -> - @_invokeActionOnRequiredModules '_autoMapSingletons' - if @is('initializing') - @log.debug("#{@constructor.publishedAs}: _autoMapSingletons") - @_state = 'auto-mapping-singletons' - # Map classes that are declared as singletons - @injector.map(singleton).asSingleton() for singleton in @singletons - - _autoCreateSingletons: -> - @_invokeActionOnRequiredModules '_autoCreateSingletons' - if @is('auto-mapping-singletons') - @log.debug("#{@constructor.publishedAs}: _autoCreateSingletons") - @_state = 'auto-creating-singletons' - # Create singleton classes - @injector.create(singleton) for singleton in @singletons - - # After all modules in the tree have been configured etc. invoke last hook - _runAfterInitializeHooks: -> - @_invokeActionOnRequiredModules '_runAfterInitializeHooks' - # Never run this hook twice - if @is('auto-creating-singletons') - @log.debug("#{@constructor.publishedAs}: afterInitialize") - @_state = 'initialized' - # Call custom lifecycle hook if existant - @afterInitialize?() - - _invokeActionOnRequiredModules: (action) -> - @app.modules[moduleId][action]?() for moduleId in @requiredModules - - _wrapLifecycleHook: (hook, wrapper) -> - this[hook] ?= -> - this[hook] = _.wrap(this[hook], wrapper) - - _setupLogger: -> - config = @_loggingConfig(@configuration) - logger = new Logger() - logger.start() if config.enabled == true - return logger - - _loggingConfig: () -> - config = {} - _.deepExtend(config, @configuration) - _.deepExtend(config, @constructor.prototype.configuration) - return config.log or {} - - _mapSpaceServices: -> - @injector.map('log').to @log - -export default Space.Module; \ No newline at end of file diff --git a/source/module.js b/source/module.js new file mode 100644 index 0000000..9147e09 --- /dev/null +++ b/source/module.js @@ -0,0 +1,282 @@ +import _ from 'underscore'; +import Logger from './logger.js'; +import {capitalize, isNil} from 'lodash'; + +const Module = Space.Object.extend('Space.Module', { + + ERRORS: { + injectorMissing: 'Instance of Space.Injector needed to initialize module.' + }, + + configuration: {}, + requiredModules: null, + // An array of paths to classes that you want to become + // singletons in your application e.g: ['Space.messaging.EventBus'] + // these are automatically mapped and created on `app.run()` + singletons: [], + injector: null, + _state: 'constructed', + + Constructor(...args) { + Space.Object.apply(this, args); + if (isNil(this.requiredModules)) { + this.requiredModules = []; + } + }, + + initialize(app, injector, isSubModule = false) { + this.app = app; + this.injector = injector; + + if (!this.is('constructed')) {return;} // Only initialize once + if (isNil(this.injector)) { + throw new Error(this.ERRORS.injectorMissing); + } + + this._state = 'configuring'; + + // Setup logger + if (!isSubModule) { + this.log = this._createLogger(); + } else { + this.log = this.injector.get('log'); + } + this.log.debug(`${this.constructor.publishedAs}: initialize`); + + // Setup basic mappings required by all modules if this the top-level module + if (!isSubModule) { + this.injector.map('Injector').to(this.injector); + this._mapSpaceServices(); + } + + // Setup required modules + for (let moduleId of this.requiredModules) { + // Create a new module instance if not already registered with the app + if (isNil(this.app.modules[moduleId])) { + ModuleClass = Module.require(moduleId, this.constructor.name); + this.app.modules[moduleId] = new ModuleClass(); + // Initialize required module + const module = this.app.modules[moduleId]; + module.initialize(this.app, this.injector, true); + } + } + + // Merge in own configuration to give the chance for overwriting. + if (isSubModule) { + _.deepExtend(this.app.configuration, this.configuration); + this.configuration = this.app.configuration; + } else { + // The app can override all other modules + _.deepExtend(this.configuration, this.constructor.prototype.configuration); + } + + // Provide lifecycle hook before any initialization has been done + if (_.isFunction(this.beforeInitialize)) { + this.beforeInitialize(); + } + + // @backward {space:base} <= 4.1.3 for Meteor package, + // Give every module access Npm + if (!isNil(Meteor) && this._isServer() && !isNil(Npm)) { + this.npm = Npm; + } + + // Top-level module + if (!isSubModule) { + this.injector.map('configuration').to(this.configuration); + this._runOnInitializeHooks(); + this._autoMapSingletons(); + this._autoCreateSingletons(); + this._runAfterInitializeHooks(); + } + }, + + start() { + if (this.is('running')) {return;} + this._runLifeCycleAction('start'); + this._state = 'running'; + }, + + reset() { + // Don't allow reseting on production env + if (this._isServer() && this._isProduction()) {return;} + if (this._isResetting) {return;} + + const restartRequired = this.is('running'); + this._isResetting = true; + if (restartRequired) {this.stop();} + this._runLifeCycleAction('reset'); + if (restartRequired) {this.start();} + + // @compability {Meteor} for Meteor package + // There is no other way to avoid reset being called multiple times + // if multiple modules require the same sub-module. + if (!isNil(Meteor)) { + Meteor.defer(() => {this._isResetting = false;}); + } + }, + + stop() { + if (this.is('stopped')) {return;} + this._runLifeCycleAction('stop', () => {}); + this._state = 'stopped'; + }, + + is(expectedState) { + return expectedState === this._state; + }, + + // Invokes the lifecycle action on all required modules, then on itself, + // calling the instance hooks before, on, and after + _runLifeCycleAction(action, func) { + this._invokeActionOnRequiredModules(action); + this.log.debug(`${this.constructor.publishedAs}: ${action}`); + + if (_.isFunction(this[`before${capitalize(action)}`])) { + this[`before${capitalize(action)}`](); + } + if (_.isFunction(func)) { + func(); + } + if (_.isFunction(this[`on${capitalize(action)}`])) { + this[`on${capitalize(action)}`](); + } + if (_.isFunction(this[`after${capitalize(action)}`])) { + this[`after${capitalize(action)}`](); + } + }, + + // Provide lifecycle hook after this module was configured and injected + _runOnInitializeHooks() { + this._invokeActionOnRequiredModules('_runOnInitializeHooks'); + // Never run this hook twice + if (this.is('configuring')) { + this.log.debug(`${this.constructor.publishedAs}: onInitialize`); + this._state = 'initializing'; + // Inject required dependencies into this module + this.injector.injectInto(this); + // Call custom lifecycle hook if existant + if (_.isFunction(this.onInitialize)) { + this.onInitialize(); + } + } + }, + + _autoMapSingletons() { + this._invokeActionOnRequiredModules('_autoMapSingletons'); + if (this.is('initializing')) { + this.log.debug(`${this.constructor.publishedAs}: _autoMapSingletons`); + this._state = 'auto-mapping-singletons'; + // Map classes that are declared as singletons + for (let singleton of this.singletons) { + this.injector.map(singleton).asSingleton(); + } + } + }, + + _autoCreateSingletons() { + this._invokeActionOnRequiredModules('_autoCreateSingletons'); + if (this.is('auto-mapping-singletons')) { + this.log.debug(`${this.constructor.publishedAs}: _autoCreateSingletons`); + this._state = 'auto-creating-singletons'; + // Create singleton classes + for (let singleton of this.singletons) { + this.injector.create(singleton); + } + } + }, + + // After all modules in the tree have been configured etc. invoke last hook + _runAfterInitializeHooks() { + this._invokeActionOnRequiredModules('_runAfterInitializeHooks'); + // Never run this hook twice + if (this.is('auto-creating-singletons')) { + this.log.debug(`${this.constructor.publishedAs}: afterInitialize`); + this._state = 'initialized'; + // Call custom lifecycle hook if existant + if (_.isFunction(this.afterInitialize)) { + this.afterInitialize(); + } + } + }, + + _invokeActionOnRequiredModules(action) { + for (let moduleId of this.requiredModules) { + if (_.isFunction(this.app.modules[moduleId][action])) { + this.app.modules[moduleId][action](); + } + } + }, + + _wrapLifecycleHook(hook, wrapper) { + if (isNil(this[hook])) { + this[hook] = () => {}; + } + this[hook] = _.wrap(this[hook], wrapper); + }, + + _createLogger() { + const config = this._getLoggingConfig(this.configuration); + const logger = new Logger(); + if (config.enabled === true) { + logger.start(); + } + return logger; + }, + + _getLoggingConfig() { + let config = {}; + _.deepExtend(config, this.configuration); + _.deepExtend(config, this.constructor.prototype.configuration); + return config.log || {}; + }, + + _mapSpaceServices() { + this.injector.map('log').to(this.log); + }, + + _isServer() { + return !(typeof window !== 'undefined' && window.document); + }, + + _isProduction() { + return process.env.NODE_ENV === 'production'; + }, + + statics: { + // ========== STATIC MODULE MANAGEMENT ============ + + // All published modules register themselves here + published: {}, + + define(moduleName, prototype = {}) { + prototype.toString = () => moduleName; // For better debugging + return this.publish(Module.extend(moduleName, prototype), moduleName); + }, + + // Publishes a module into the space environment to make it + // visible and requireable for other modules and the application + publish(module, identifier) { + module.publishedAs = module.name = identifier; + if (!isNil(Module.published[identifier])) { + throw new Error(`Two modules tried to be published as <${identifier}>`); + } else { + Module.published[identifier] = module; + return Module.published[identifier]; + } + }, + + // Retrieve a module by identifier + require(requiredModule, requestingModule) { + const module = Module.published[requiredModule]; + if (isNil(module)) { + throw new Error(`Could not find module <${requiredModule}> required by + <${requestingModule}>`); + } else { + return module; + } + } + } +}); + +export default Module; From fcd93889b06bc8bae95c50254febd2b7df991c2f Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 14:28:42 +0100 Subject: [PATCH 035/111] Replaces Space.Object with imported SpaceObject module --- source/error.js | 5 +++-- source/logger.js | 4 +++- source/loggers/adapter.js | 4 +++- source/module.js | 3 ++- source/struct.coffee | 5 +++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/source/error.js b/source/error.js index 51f960b..1067e4d 100644 --- a/source/error.js +++ b/source/error.js @@ -1,6 +1,7 @@ import _ from 'underscore'; import {optional, Integer} from 'simplecheck'; import Struct from './struct.js'; +import SpaceObject from './object.coffee'; let IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; @@ -24,7 +25,7 @@ SpaceError.prototype = new IntermediateInheritor(); _.extend( SpaceError.prototype, // target Struct.prototype, - _.omit(Space.Object.prototype, 'toString'), + _.omit(SpaceObject.prototype, 'toString'), { message: '', fields() { @@ -48,7 +49,7 @@ _.extend( } ); -_.extend(SpaceError, _.omit(Space.Object, 'toString'), { +_.extend(SpaceError, _.omit(SpaceObject, 'toString'), { __keepToStringMethod__: true // Do not override #toString method }); diff --git a/source/logger.js b/source/logger.js index d28ea89..7a555f3 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,4 +1,6 @@ -const Logger = Space.Object.extend('Space.Logger', { +import SpaceObject from './object.coffee'; + +const Logger = SpaceObject.extend('Space.Logger', { STATES: { stopped: 'stopped', diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index 6a10d66..6ecf9b3 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -1,4 +1,6 @@ -const LoggingAdapter = Space.Object.extend('Space.Logger.LoggingAdapter', { +import SpaceObject from '../object.coffee'; + +const LoggingAdapter = SpaceObject.extend('Space.Logger.LoggingAdapter', { _lib: null, Constructor(lib) { diff --git a/source/module.js b/source/module.js index 9147e09..55a42e8 100644 --- a/source/module.js +++ b/source/module.js @@ -1,8 +1,9 @@ import _ from 'underscore'; import Logger from './logger.js'; import {capitalize, isNil} from 'lodash'; +import SpaceObject from './object.coffee'; -const Module = Space.Object.extend('Space.Module', { +const Module = SpaceObject.extend('Space.Module', { ERRORS: { injectorMissing: 'Instance of Space.Injector needed to initialize module.' diff --git a/source/struct.coffee b/source/struct.coffee index ed4b29b..09ba1ed 100644 --- a/source/struct.coffee +++ b/source/struct.coffee @@ -1,7 +1,8 @@ import _ from 'underscore'; import {ensure} from 'simplecheck'; +import SpaceObject from './object.coffee'; -class Space.Struct extends Space.Object +class Struct extends SpaceObject @fields: {} @@ -19,4 +20,4 @@ class Space.Struct extends Space.Object # Use the fields configuration to check given data during runtime _checkFields: (data) -> ensure data, @fields() -export default Space.Struct; +export default Struct; From 0420d488a1f13a5f3ad9c5fd76925c9259d36557 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 14:29:23 +0100 Subject: [PATCH 036/111] Exports SpaceObject and removes Object from Space global --- source/object.coffee | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/source/object.coffee b/source/object.coffee index e470ed6..fcf1dad 100644 --- a/source/object.coffee +++ b/source/object.coffee @@ -1,7 +1,7 @@ import _ from 'underscore'; import {ensure, oneOf, anything} from 'simplecheck'; -class Space.Object +class SpaceObject # ============= PUBLIC PROTOTYPE ============== # @@ -43,47 +43,47 @@ class Space.Object # # There are various ways you can call this method: # - # 1. Space.Object.extend() + # 1. SpaceObject.extend() # -------------------------------------------- # Creates an anonymous child class without extra prototype properties. - # Basically the same as `class extend Space.Object` in coffeescript + # Basically the same as `class extend SpaceObject` in coffeescript # - # 2. Space.Object.extend(className) + # 2. SpaceObject.extend(className) # -------------------------------------------- # Creates a named child class without extra prototype properties. - # Basically the same as `class ClassName extend Space.Object` in coffeescript + # Basically the same as `class ClassName extend SpaceObject` in coffeescript # - # 3. Space.Object.extend(classPath) + # 3. SpaceObject.extend(classPath) # -------------------------------------------- # Creates a child class with fully qualified class path like "my.custom.Class" # assigned and registered internally so that Space.resolvePath can find it. # This also assigns the class path as type, which can be used for serialization # - # 4. Space.Object.extend({ prop: 'first', … }) + # 4. SpaceObject.extend({ prop: 'first', … }) # -------------------------------------------- # Creates an anonymous child class with extra prototype properties. # Same as: - # class extend Space.Object + # class extend SpaceObject # prop: 'first' # - # 5. Space.Object.extend(namespace, className) + # 5. SpaceObject.extend(namespace, className) # -------------------------------------------- - # Creates a named class which inherits from Space.Object and assigns + # Creates a named class which inherits from SpaceObject and assigns # it to the given namespace object. # - # 6. Space.Object.extend(className, prototype) + # 6. SpaceObject.extend(className, prototype) # -------------------------------------------- - # Creates a named class which inherits from Space.Object and extra prototype + # Creates a named class which inherits from SpaceObject and extra prototype # properties which are assigned to the new class # - # 7. Space.Object.extend(classPath, prototype) + # 7. SpaceObject.extend(classPath, prototype) # -------------------------------------------- - # Creates a registered class which inherits from Space.Object and extra prototype + # Creates a registered class which inherits from SpaceObject and extra prototype # properties which are assigned to the new class # - # 8. Space.Object.extend(namespace, className, prototype) + # 8. SpaceObject.extend(namespace, className, prototype) # -------------------------------------------- - # Creates a named class which inherits from Space.Object, has extra prototype + # Creates a named class which inherits from SpaceObject, has extra prototype # properties and is assigned to the given namespace. @extend: (args...) -> @@ -206,7 +206,7 @@ class Space.Object namespace[className] = this # Create and instance of the class that this method is called on - # e.g.: Space.Object.create() would return an instance of Space.Object + # e.g.: SpaceObject.create() would return an instance of SpaceObject @create: -> # Use a wrapper class to hand the constructor arguments # to the context class that #create was called on @@ -304,4 +304,4 @@ class Space.Object # Set non-existing props and override existing methods prototype[key] = value -export default Space.Object; \ No newline at end of file +export default SpaceObject; \ No newline at end of file From 5b7724c1821df754aaa5cc2031986213fa983b44 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 14:33:05 +0100 Subject: [PATCH 037/111] Converts Application and removes it from Space global --- source/application.coffee | 23 ----------------------- source/application.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 23 deletions(-) delete mode 100644 source/application.coffee create mode 100644 source/application.js diff --git a/source/application.coffee b/source/application.coffee deleted file mode 100644 index 92ce1a5..0000000 --- a/source/application.coffee +++ /dev/null @@ -1,23 +0,0 @@ -import _ from 'underscore'; - -class Space.Application extends Space.Module - - configuration: { - appId: null - } - - @define: (appName, prototype) -> - prototype.toString = -> appName # For better debugging - return @extend appName, prototype - - constructor: (options={}) -> - super - @modules = {} - @configuration = options.configuration || {} - @constructor.publishedAs = @constructor.name - @initialize this, options.injector ? new Space.Injector() - - # Make it possible to override configuration (at any nested level) - configure: (options) -> _.deepExtend @configuration, options - -export default Space.Application; \ No newline at end of file diff --git a/source/application.js b/source/application.js new file mode 100644 index 0000000..594114c --- /dev/null +++ b/source/application.js @@ -0,0 +1,31 @@ +import _ from 'underscore'; +import Module from './module.js'; + +const Application = Module.extend('Space.Application', { + + statics: { + define(classPath, prototype) { + prototype.toString = () => appName; // For better debugging + return this.extend(classPath, prototype); + } + }, + + configuration: { + appId: null + }, + + Constructor(options = {}) { + Module.call(this, options); + this.modules = {}; + this.configuration = options.configuration || {}; + this.constructor.publishedAs = this.constructor.name; + this.initialize(this, options.injector || new Space.Injector()); + }, + + // Make it possible to override configuration (at any nested level) + configure(options) { + _.deepExtend(this.configuration, options); + } +}); + +export default Application; From 8fc3af0a025e6d211706b308ed3fac896da03b81 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 15:43:46 +0100 Subject: [PATCH 038/111] Fixes import statement pointing to incorrect file --- source/error.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/error.js b/source/error.js index 1067e4d..6278e30 100644 --- a/source/error.js +++ b/source/error.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import {optional, Integer} from 'simplecheck'; -import Struct from './struct.js'; +import Struct from './struct.coffee'; import SpaceObject from './object.coffee'; let IntermediateInheritor = function() {}; From 18c97c50732cd028e86909ffa50610b4bdb362ff Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:32:29 +0100 Subject: [PATCH 039/111] Converts bdd-api --- source/testing/bdd-api.coffee | 26 ----------------------- source/testing/bdd-api.js | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 26 deletions(-) delete mode 100644 source/testing/bdd-api.coffee create mode 100644 source/testing/bdd-api.js diff --git a/source/testing/bdd-api.coffee b/source/testing/bdd-api.coffee deleted file mode 100644 index 4bb1772..0000000 --- a/source/testing/bdd-api.coffee +++ /dev/null @@ -1,26 +0,0 @@ -registeredBddApis = [] - -Space.Module.registerBddApi = (api) -> registeredBddApis.push api - -Space.Module.test = Space.Application.test = (systemUnderTest, app=null) -> - throw new Error 'Cannot test ' unless systemUnderTest? - testApi = null - isModule = isSubclassOf(this, Space.Module) - isApplication = isSubclassOf(this, Space.Application) - - # BDD API relies on dependency injection provided by Application - if !app? - if isApplication - app = new this() - else - app = new (Space.Application.define('TestApp', { - configuration: { appId: 'testApp' }, - requiredModules: [this.publishedAs] - })) - - for api in registeredBddApis - returnValue = api(app, systemUnderTest) - testApi = returnValue if returnValue? - - if not testApi? then throw new Error "No testing API found for #{systemUnderTest}" - return testApi \ No newline at end of file diff --git a/source/testing/bdd-api.js b/source/testing/bdd-api.js new file mode 100644 index 0000000..e665aae --- /dev/null +++ b/source/testing/bdd-api.js @@ -0,0 +1,40 @@ +import {isNil} from 'lodash'; +import Module from '../module.js'; +import Application from '../application.js'; + +const registeredBddApis = []; + +Module.registerBddApi = api => registeredBddApis.push(api); + +Module.test = Application.test = function(systemUnderTest, app = null) { + if (sisNil(ystemUnderTest)) { + throw new Error('Cannot test '); + } + let testApi = null; + const isModule = isSubclassOf(this, Module); + const isApplication = isSubclassOf(this, Application); + + // BDD API relies on dependency injection provided by Application + if (isNil(app)) { + if (isApplication) { + app = new this(); + } else { + app = new (Application.define('TestApp', { + configuration: { + appId: 'testApp' + }, + requiredModules: [this.publishedAs] + })); + } + } + for (let api of registeredBddApis) { + returnValue = api(app, systemUnderTest); + if (!isNil(returnValue)) { + testApi = returnValue; + } + } + if (isNil(testApi)) { + throw new Error(`No testing API found for ${systemUnderTest}`); + } + return testApi; +}; From 6298b903a2714862219d74a5e010deadff2435a2 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:35:03 +0100 Subject: [PATCH 040/111] Combines namespace and helpers in to single Space file --- source/{helpers.js => space.js} | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) rename source/{helpers.js => space.js} (78%) diff --git a/source/helpers.js b/source/space.js similarity index 78% rename from source/helpers.js rename to source/space.js index 17a3659..5403e0d 100644 --- a/source/helpers.js +++ b/source/space.js @@ -1,8 +1,23 @@ import {isNil, get, capitalize} from 'lodash'; -import Space from './namespace.js'; -const global = this; +class Namespace { + constructor(path) { + this._path = path; + } + getPath() { + return this._path; + } + toString() { + return this._path; + } +} +// Define namespace for the space framework +const Space = new Namespace('Space'); +Space.Namespace = Namespace; +Space.registry = {}; + +const global = this; // Resolves a (possibly nested) path to a global object // Returns the object or null (if not found) Space.resolvePath = function(path) { @@ -36,3 +51,5 @@ Space.namespace = function(id) { // @backward {space:base} <= 4.1.3 Space.capitalizeString = capitalize; + +export default Space; From 399f602b7939df7c6bea2c496798668b3a017766 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:35:46 +0100 Subject: [PATCH 041/111] Fixes issue with unexportable SpaceObject module with ES6 --- source/object.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/object.coffee b/source/object.coffee index fcf1dad..8e56c27 100644 --- a/source/object.coffee +++ b/source/object.coffee @@ -1,5 +1,6 @@ import _ from 'underscore'; import {ensure, oneOf, anything} from 'simplecheck'; +import Space from './space.js'; class SpaceObject @@ -30,7 +31,7 @@ class SpaceObject hasMixin: (mixin) -> _.contains(@constructor._getAppliedMixins(), mixin) # This method needs to stay separate from the constructor so that - # Space.Error can use it too! + # SpaceError can use it too! _invokeConstructionCallbacks: -> # Let mixins initialize themselves on construction for mixin in @constructor._getAppliedMixins() @@ -193,8 +194,6 @@ class SpaceObject return Child - @toString: -> @classPath - @type: (@classPath) -> # Register this class with its class path Space.registry[@classPath] = this @@ -304,4 +303,9 @@ class SpaceObject # Set non-existing props and override existing methods prototype[key] = value -export default SpaceObject; \ No newline at end of file +# Incompatible with ES6 import/export without fallback +# Fallback to original result from toString if classPath is not defined +toStringFallback = SpaceObject.toString.bind(SpaceObject) +SpaceObject.toString = -> @classPath || toStringFallback() + +export default SpaceObject; From b478b9caef200deb4f0e49545805c895d7b1f453 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:36:20 +0100 Subject: [PATCH 042/111] Removes namespace --- source/namespace.js | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 source/namespace.js diff --git a/source/namespace.js b/source/namespace.js deleted file mode 100644 index 743247e..0000000 --- a/source/namespace.js +++ /dev/null @@ -1,18 +0,0 @@ -class Namespace { - constructor(path) { - this._path = path; - } - getPath() { - return this._path; - } - toString() { - return this._path; - } -} - -// Define global namespace for the space framework -this.Space = new Namespace('Space'); -this.Space.Namespace = Namespace; -this.Space.registry = {}; - -export default Space; From 9a1a25ac457f223aa2f1b3db3fb3a5ff43aaeaab Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:37:55 +0100 Subject: [PATCH 043/111] Imports Space and replaces global related calls --- source/application.js | 3 ++- source/configuration.js | 2 ++ source/injector-annotations.js | 5 +++-- source/injector.js | 3 +-- source/meteor.js | 3 +++ source/module.js | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 source/meteor.js diff --git a/source/application.js b/source/application.js index 594114c..0c32728 100644 --- a/source/application.js +++ b/source/application.js @@ -1,5 +1,6 @@ import _ from 'underscore'; import Module from './module.js'; +import {Injector} from './injector.js'; const Application = Module.extend('Space.Application', { @@ -19,7 +20,7 @@ const Application = Module.extend('Space.Application', { this.modules = {}; this.configuration = options.configuration || {}; this.constructor.publishedAs = this.constructor.name; - this.initialize(this, options.injector || new Space.Injector()); + this.initialize(this, options.injector || new Injector()); }, // Make it possible to override configuration (at any nested level) diff --git a/source/configuration.js b/source/configuration.js index 35ec25a..83ba0fc 100644 --- a/source/configuration.js +++ b/source/configuration.js @@ -1,3 +1,5 @@ +import Space from './space.js'; + if (Meteor.isServer) { Space.getenv = Npm.require('getenv'); } diff --git a/source/injector-annotations.js b/source/injector-annotations.js index 7cc9834..7fefd19 100644 --- a/source/injector-annotations.js +++ b/source/injector-annotations.js @@ -1,7 +1,8 @@ import _ from 'underscore'; import {isNil} from 'lodash'; +import Space from './space.js'; -this.Space.Dependency = function(propertyName, dependencyId) { +Space.Dependency = function(propertyName, dependencyId) { return function(target) { const proto = target.prototype; if (proto.dependencies && !proto.hasOwnProperty('Dependencies')) { @@ -13,7 +14,7 @@ this.Space.Dependency = function(propertyName, dependencyId) { }; }; -this.Space.RequireModule = function(moduleId) { +Space.RequireModule = function(moduleId) { return function(target) { const proto = target.prototype; if (proto.requiredModules && !proto.hasOwnProperty('RequiredModules')) { diff --git a/source/injector.js b/source/injector.js index 88d9728..9cb58c2 100644 --- a/source/injector.js +++ b/source/injector.js @@ -1,6 +1,7 @@ import _ from 'underscore'; import {isNil} from 'lodash'; import SpaceError from './error.js'; +import Space from './space.js'; InjectionError = SpaceError.extend('Space.InjectionError'); @@ -314,8 +315,6 @@ Injector.DEFAULT_PROVIDERS = { toSingleton: SingletonProvider }; -Space.Injector = Injector; - export { InjectionError as InjectionError, Injector as Injector, diff --git a/source/meteor.js b/source/meteor.js new file mode 100644 index 0000000..dac6456 --- /dev/null +++ b/source/meteor.js @@ -0,0 +1,3 @@ +import {Space} from './index.js' ; + +this.Space = Space; diff --git a/source/module.js b/source/module.js index 55a42e8..fc7d467 100644 --- a/source/module.js +++ b/source/module.js @@ -19,7 +19,7 @@ const Module = SpaceObject.extend('Space.Module', { _state: 'constructed', Constructor(...args) { - Space.Object.apply(this, args); + SpaceObject.apply(this, args); if (isNil(this.requiredModules)) { this.requiredModules = []; } From f165a99a945a5dc14a5cc6dcff79ccbf8b27c8de Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:40:57 +0100 Subject: [PATCH 044/111] Adds index --- source/index.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 source/index.js diff --git a/source/index.js b/source/index.js new file mode 100644 index 0000000..ef4d559 --- /dev/null +++ b/source/index.js @@ -0,0 +1,35 @@ +import Space from './space.js'; +import SpaceObject from './object.coffee'; +import Struct from './struct.coffee'; +import SpaceError from './error.js'; +import {Injector, InjectionError} from './injector.js'; +import Logger from './logger.js'; +import LoggingAdapter from './loggers/adapter.js'; +import ConsoleLogger from './loggers/console-adapter.js'; +import Module from './module.js'; +import Application from './application.js'; + +Space.Object = SpaceObject; +Space.Struct = Struct; +Space.Error = SpaceError; +Space.Injector = Injector; +Space.InjectionError = InjectionError; +Space.Logger = Logger; +Space.LoggingAdapter = LoggingAdapter; +Space.ConsoleLogger = ConsoleLogger; +Space.Module = Module; +Space.Application = Application; + +export { + SpaceObject as SpaceObject, + Struct as Struct, + SpaceError as SpaceError, + Injector as Injector, + InjectionError as InjectionError, + Logger as Logger, + LoggingAdapter as LoggingAdapter, + ConsoleLogger as ConsoleLogger, + Module as Module, + Application as Application, + Space as Space +}; From 15fec86ccf9dda2b41a5af2d575d7254beaca606 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:41:30 +0100 Subject: [PATCH 045/111] Updates file paths on meteor package --- package.js | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/package.js b/package.js index a9115f9..88add04 100644 --- a/package.js +++ b/package.js @@ -20,27 +20,41 @@ Package.onUse(function(api) { ]); api.addFiles([ - 'source/lib/underscore_deep_extend_mixin.js', - 'source/namespace.coffee', - 'source/helpers.coffee', + 'source/lib/underscore-deep-extend-mixin.js', + 'source/space.js', 'source/configuration.js', 'source/object.coffee', 'source/logger.js', 'source/struct.coffee', 'source/error.js', - 'source/injector.coffee', - 'source/injector_annotations.coffee', - 'source/module.coffee', - 'source/application.coffee', + 'source/injector.js', + 'source/injector-annotations.js', + 'source/module.js', + 'source/application.js', 'source/loggers/adapter.js', - 'source/loggers/console-adapter.js' + 'source/loggers/console-adapter.js', + 'source/index.js', + 'source/meteor.js' ]); // Test helpers api.addFiles([ - 'source/testing/bdd-api.coffee' + 'source/testing/bdd-api.js' ]); + api.export([ + 'SpaceObject', + 'Struct', + 'SpaceError', + 'Injector', + 'InjectionError', + 'Logger', + 'LoggingAdapter', + 'ConsoleLogger', + 'Module', + 'Application', + 'Space' + ]); }); Package.onTest(function(api) { From 09b729b369dfd7574685932e6af908da02b33fa5 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 16:42:14 +0100 Subject: [PATCH 046/111] Imports modules on tests --- tests/integration/application_with_modules.spec.js | 4 ++-- tests/integration/lifecycle_hooks.tests.js | 4 ++-- tests/integration/module.regressions.js | 5 +++-- tests/integration/requiring-modules.tests.js | 4 ++-- tests/unit/application.unit.coffee | 6 +++--- tests/unit/helpers.unit.coffee | 2 +- tests/unit/injector.unit.coffee | 3 ++- tests/unit/injector_annotations.unit.js | 1 + tests/unit/logger.tests.js | 5 +++-- tests/unit/module.unit.coffee | 4 ++-- tests/unit/object.unit.coffee | 1 + 11 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tests/integration/application_with_modules.spec.js b/tests/integration/application_with_modules.spec.js index 006e311..21e040a 100644 --- a/tests/integration/application_with_modules.spec.js +++ b/tests/integration/application_with_modules.spec.js @@ -1,5 +1,5 @@ -import Module from '../../source/module.coffee'; -import Application from '../../source/application.coffee'; +import Module from '../../source/module.js'; +import Application from '../../source/application.js'; describe('Building applications based on modules', function() { diff --git a/tests/integration/lifecycle_hooks.tests.js b/tests/integration/lifecycle_hooks.tests.js index bee945b..bd623bf 100644 --- a/tests/integration/lifecycle_hooks.tests.js +++ b/tests/integration/lifecycle_hooks.tests.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -import Module from '../../source/module.coffee'; -import Application from '../../source/application.coffee'; +import Module from '../../source/module.js'; +import Application from '../../source/application.js'; describe("Space.base - Application lifecycle hooks", function() { diff --git a/tests/integration/module.regressions.js b/tests/integration/module.regressions.js index cd80255..72358ea 100644 --- a/tests/integration/module.regressions.js +++ b/tests/integration/module.regressions.js @@ -1,6 +1,7 @@ -import Module from '../../source/module.coffee'; +import Module from '../../source/module.js'; import SpaceObject from '../../source/object.coffee'; -import {Injector} from '../../source/injector.coffee'; +import {Injector} from '../../source/injector.js'; +import Space from '../../source/space.js'; describe("Module - regressions", function() { diff --git a/tests/integration/requiring-modules.tests.js b/tests/integration/requiring-modules.tests.js index 9b31806..66b8cf7 100644 --- a/tests/integration/requiring-modules.tests.js +++ b/tests/integration/requiring-modules.tests.js @@ -1,5 +1,5 @@ -import Module from '../../source/module.coffee'; -import Application from '../../source/application.coffee'; +import Module from '../../source/module.js'; +import Application from '../../source/application.js'; describe("Space.base - Requiring modules in other modules and apps", function() { diff --git a/tests/unit/application.unit.coffee b/tests/unit/application.unit.coffee index 3f2498f..96e5454 100644 --- a/tests/unit/application.unit.coffee +++ b/tests/unit/application.unit.coffee @@ -1,6 +1,6 @@ -import Application from '../../source/application.coffee'; -import Module from '../../source/module.coffee'; -import {Injector} from '../../source/injector.coffee'; +import Application from '../../source/application.js'; +import Module from '../../source/module.js'; +import {Injector} from '../../source/injector.js'; describe 'Application', -> diff --git a/tests/unit/helpers.unit.coffee b/tests/unit/helpers.unit.coffee index 1403215..9355ec2 100644 --- a/tests/unit/helpers.unit.coffee +++ b/tests/unit/helpers.unit.coffee @@ -1,4 +1,4 @@ -import Space from '../../source/namespace.coffee'; +import Space from '../../source/space.js'; global = this diff --git a/tests/unit/injector.unit.coffee b/tests/unit/injector.unit.coffee index 652f675..eef088d 100644 --- a/tests/unit/injector.unit.coffee +++ b/tests/unit/injector.unit.coffee @@ -1,5 +1,6 @@ -import {Injector} from '../../source/injector.coffee'; +import {Injector} from '../../source/injector.js'; import SpaceObject from '../../source/object.coffee'; +import Space from '../../source/space.js'; global = this diff --git a/tests/unit/injector_annotations.unit.js b/tests/unit/injector_annotations.unit.js index 3cedac6..5304e28 100644 --- a/tests/unit/injector_annotations.unit.js +++ b/tests/unit/injector_annotations.unit.js @@ -1,3 +1,4 @@ +import Space from '../../source/space.js'; describe('Injector annotations', function() { diff --git a/tests/unit/logger.tests.js b/tests/unit/logger.tests.js index 475e925..2bd3ac5 100644 --- a/tests/unit/logger.tests.js +++ b/tests/unit/logger.tests.js @@ -1,5 +1,6 @@ import Logger from '../../source/logger.js'; import LoggingAdapter from '../../source/loggers/adapter.js'; +import SpaceObject from '../../source/object.coffee'; const TestAdapter = LoggingAdapter.extend('TestAdapter', { Constructor(lib) { @@ -20,8 +21,8 @@ describe("Logger", function() { this.logger = new Logger(); }); - it('extends Space.Object', () => { - expect(Logger).to.extend(Space.Object); + it('extends SpaceObject', () => { + expect(Logger).to.extend(SpaceObject); }); it("is available of both client and server", () => { diff --git a/tests/unit/module.unit.coffee b/tests/unit/module.unit.coffee index f31a82f..eb9d70a 100644 --- a/tests/unit/module.unit.coffee +++ b/tests/unit/module.unit.coffee @@ -1,6 +1,6 @@ import SpaceObject from '../../source/object.coffee'; -import Module from '../../source/module.coffee'; -import {Injector} from '../../source/injector.coffee'; +import Module from '../../source/module.js'; +import {Injector} from '../../source/injector.js'; describe 'Module', -> diff --git a/tests/unit/object.unit.coffee b/tests/unit/object.unit.coffee index f649165..313a909 100644 --- a/tests/unit/object.unit.coffee +++ b/tests/unit/object.unit.coffee @@ -1,4 +1,5 @@ import SpaceObject from '../../source/object.coffee'; +import Space from '../../source/space.js'; describe 'SpaceObject', -> From dbbb128beb62283be95c4e6aac0677785f3b7a75 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 17:22:41 +0100 Subject: [PATCH 047/111] Updates CircleCI --- circle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/circle.yml b/circle.yml index bb261ea..87aadc7 100644 --- a/circle.yml +++ b/circle.yml @@ -5,6 +5,7 @@ machine: - curl https://install.meteor.com | /bin/sh dependencies: pre: + - npm install - npm install -g spacejam test: override: From 08db3631203eaa367fb39a266fa08e9bbcc3ffdc Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 17:28:16 +0100 Subject: [PATCH 048/111] Adds getnv directly via import --- source/configuration.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/configuration.js b/source/configuration.js index 83ba0fc..686139a 100644 --- a/source/configuration.js +++ b/source/configuration.js @@ -1,5 +1,6 @@ import Space from './space.js'; +import getenv from 'getenv'; if (Meteor.isServer) { - Space.getenv = Npm.require('getenv'); + Space.getenv = getenv; } From 9bb64d5ccad10ad6fea06e72e19d8012f7cbc064 Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 17:38:23 +0100 Subject: [PATCH 049/111] Removes getenv and configuration, Pins node to >=4.0.0 --- package.js | 1 - package.json | 9 ++++++++- source/configuration.js | 6 ------ 3 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 source/configuration.js diff --git a/package.js b/package.js index 88add04..028da22 100644 --- a/package.js +++ b/package.js @@ -22,7 +22,6 @@ Package.onUse(function(api) { api.addFiles([ 'source/lib/underscore-deep-extend-mixin.js', 'source/space.js', - 'source/configuration.js', 'source/object.coffee', 'source/logger.js', 'source/struct.coffee', diff --git a/package.json b/package.json index 2321e87..8b94fde 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,20 @@ "name": "space-base", "version": "4.1.4", "description": "Modular Application Architecture", + "engines": { + "node": ">=4.0.0" + }, "dependencies": { + "underscore": "1.8.*", + "simplecheck": "git://github.com/qejk/simplecheck.git", + "lodash": "4.17.*" + }, + "devDependencies": { "getenv": "0.7.*", "underscore": "1.8.*", "simplecheck": "git://github.com/qejk/simplecheck.git", "lodash": "4.17.*" }, - "devDependencies": {}, "scripts": {}, "author": "Dominik Guzei, Rhys Bartels-Waller, Darko Mijic, Adam Desivi", "license": "MIT" diff --git a/source/configuration.js b/source/configuration.js deleted file mode 100644 index 686139a..0000000 --- a/source/configuration.js +++ /dev/null @@ -1,6 +0,0 @@ -import Space from './space.js'; -import getenv from 'getenv'; - -if (Meteor.isServer) { - Space.getenv = getenv; -} From 15cdb0c7f466415899ae2ead8ab54f269365299a Mon Sep 17 00:00:00 2001 From: qejk Date: Tue, 10 Jan 2017 17:38:41 +0100 Subject: [PATCH 050/111] Updates CircleCI --- circle.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 87aadc7..5df0a5e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,12 +1,16 @@ machine: node: - version: 0.10.33 + version: 4.0.0 pre: - curl https://install.meteor.com | /bin/sh dependencies: + cache_directories: + - node_modules pre: - - npm install - npm install -g spacejam + override: + - npm prune && npm install + test: override: - spacejam test-packages ./ From 9e4637cd0fd6b6634357d2b7f7bc1f4adef8fadf Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 08:34:36 +0100 Subject: [PATCH 051/111] Updates npm dependencies and CircleCI --- circle.yml | 2 -- package.json | 7 +------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/circle.yml b/circle.yml index 5df0a5e..2ab0262 100644 --- a/circle.yml +++ b/circle.yml @@ -8,8 +8,6 @@ dependencies: - node_modules pre: - npm install -g spacejam - override: - - npm prune && npm install test: override: diff --git a/package.json b/package.json index 8b94fde..627554b 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,7 @@ "simplecheck": "git://github.com/qejk/simplecheck.git", "lodash": "4.17.*" }, - "devDependencies": { - "getenv": "0.7.*", - "underscore": "1.8.*", - "simplecheck": "git://github.com/qejk/simplecheck.git", - "lodash": "4.17.*" - }, + "devDependencies": {}, "scripts": {}, "author": "Dominik Guzei, Rhys Bartels-Waller, Darko Mijic, Adam Desivi", "license": "MIT" From 35e5b629d846a2488684cfded32b8f530c9753b4 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:10:48 +0100 Subject: [PATCH 052/111] Converts Struct unit test --- tests/unit/struct.unit.coffee | 66 ---------------------------- tests/unit/struct.unit.js | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 66 deletions(-) delete mode 100644 tests/unit/struct.unit.coffee create mode 100644 tests/unit/struct.unit.js diff --git a/tests/unit/struct.unit.coffee b/tests/unit/struct.unit.coffee deleted file mode 100644 index d66435c..0000000 --- a/tests/unit/struct.unit.coffee +++ /dev/null @@ -1,66 +0,0 @@ -import {MatchError, Integer} from 'simplecheck'; -import Struct from '../../source/struct.coffee'; -import SpaceObject from '../../source/object.coffee'; - -describe 'Struct', -> - class MyTestStruct extends Struct - @type 'MyTestStruct' - fields: -> name: String, age: Integer - - class MyExtendedTestStruct extends MyTestStruct - @type 'MyExtendedTestStruct' - fields: -> - fields = super() - fields.extra = Integer - return fields - - it "is a SpaceObject", -> - expect(Struct).to.extend(SpaceObject) - - it "calls the super constructor", -> - constructorSpy = sinon.spy(SpaceObject.prototype, 'constructor') - data = {} - struct = new Struct(data) - expect(constructorSpy).to.have.been.calledWithExactly(data) - expect(constructorSpy).to.have.been.calledOn(struct) - constructorSpy.restore() - - describe 'defining fields', -> - - it 'assigns the properties to the instance', -> - properties = name: 'Dominik', age: 26 - instance = new MyTestStruct properties - expect(instance).toMatch properties - - it 'provides a method to cast to plain object', -> - instance = new MyTestStruct name: 'Dominik', age: 26 - copy = instance.toPlainObject() - expect(copy.name).to.equal 'Dominik' - expect(copy.age).to.equal 26 - expect(copy).to.be.an.object - expect(copy).not.to.be.instanceof MyTestStruct - - it 'throws a match error if a property is of wrong type', -> - expect(-> new MyTestStruct name: 5, age: 26).to.throw MatchError - - it 'throws a match error if additional properties are given', -> - expect(-> new MyTestStruct name: 5, age: 26, extra: 0).to.throw MatchError - - it 'throws a match error if a property is missing', -> - expect(-> new MyTestStruct name: 5).to.throw MatchError - - it 'allows to extend the fields of base classes', -> - expect(-> new MyExtendedTestStruct name: 'test', age: 26, extra: 0) - .not.to.throw MatchError - - # TODO: remove when breaking change is made for next major version: - it 'stays backward compatible with static fields api', -> - class StaticFieldsStruct extends Struct - @fields: { name: String, age: Integer } - - properties = name: 'Dominik', age: 26 - instance = new StaticFieldsStruct properties - expect(instance).toMatch properties - expect(-> new StaticFieldsStruct name: 5).to.throw MatchError - - diff --git a/tests/unit/struct.unit.js b/tests/unit/struct.unit.js new file mode 100644 index 0000000..4f50b49 --- /dev/null +++ b/tests/unit/struct.unit.js @@ -0,0 +1,81 @@ +import {MatchError, Integer} from 'simplecheck'; +import Struct from '../../source/struct.coffee'; +import SpaceObject from '../../source/object.coffee'; + +describe('Struct', function() { + + class MyTestStruct extends Struct { + fields() { + return {name: String, age: Integer}; + } + } + + class MyExtendedTestStruct extends MyTestStruct { + fields() { + const fields = super.fields(); + fields.extra = Integer; + return fields; + } + } + + it("is a SpaceObject", () => { + expect(Struct).to.extend(SpaceObject); + }); + + it("calls the super constructor", () => { + const constructorSpy = sinon.spy(SpaceObject.prototype, 'constructor'); + const data = {}; + const struct = new Struct(data); + expect(constructorSpy).to.have.been.calledWithExactly(data); + expect(constructorSpy).to.have.been.calledOn(struct); + constructorSpy.restore(); + }); + + describe('defining fields', () => { + it('assigns the properties to the instance', () => { + const properties = {name: 'Dominik', age: 26}; + const instance = new MyTestStruct(properties); + expect(instance).toMatch(properties); + }); + + it('provides a method to cast to plain object', () => { + const instance = new MyTestStruct({name: 'Dominik', age: 26}); + const copy = instance.toPlainObject(); + expect(copy.name).to.equal('Dominik'); + expect(copy.age).to.equal(26); + expect(copy).to.be.an.object; + expect(copy).not.to.be.instanceof(MyTestStruct); + }); + + it('throws a match error if a property is of wrong type', () => { + const properties = {name: 5, age: 26}; + expect(() => new MyTestStruct(properties)).to.throw(MatchError); + }); + + it('throws a match error if additional properties are given', () => { + const properties = {name: 5, age: 26, extra: 0}; + expect(() => new MyTestStruct(properties)).to.throw(MatchError); + }); + + it('throws a match error if a property is missing', () => { + const properties = {name: 5}; + expect(() => new MyTestStruct(properties)).to.throw(MatchError); + }); + + it('allows to extend the fields of base classes', () => { + const properties = {name: 'test', age: 26, extra: 0}; + expect(() => new MyExtendedTestStruct(properties)).not.to.throw(MatchError); + }); + + // TODO: remove when breaking change is made for next major version: + it('stays backward compatible with static fields api', () => { + class StaticFieldsStruct extends Struct {} + StaticFieldsStruct.fields = {name: String, age: Integer}; + + const properties = {name: 'Dominik', age: 26}; + const instance = new StaticFieldsStruct(properties); + expect(instance).toMatch(properties); + expect(() => new StaticFieldsStruct({name: 5})).to.throw(MatchError); + }); + }); +}); From ed11964360500d0054fce8aefb7406d65d77e3d1 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:11:13 +0100 Subject: [PATCH 053/111] Converts SpaceObject unit test --- tests/unit/object.unit.coffee | 257 ------------------------ tests/unit/object.unit.js | 354 ++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+), 257 deletions(-) delete mode 100644 tests/unit/object.unit.coffee create mode 100644 tests/unit/object.unit.js diff --git a/tests/unit/object.unit.coffee b/tests/unit/object.unit.coffee deleted file mode 100644 index 313a909..0000000 --- a/tests/unit/object.unit.coffee +++ /dev/null @@ -1,257 +0,0 @@ -import SpaceObject from '../../source/object.coffee'; -import Space from '../../source/space.js'; - -describe 'SpaceObject', -> - - beforeEach -> @namespace = {} - - describe 'extending', -> - - it 'creates and returns a subclass', -> - SpaceObject.extend(@namespace, 'MyClass') - expect(@namespace.MyClass).to.extend SpaceObject - - it 'applies the arguments to the super constructor', -> - [first, second, third] = ['first', 2, {}] - spy = sinon.spy() - SpaceObject.extend @namespace, 'Base', { - Constructor: -> spy.apply this, arguments - } - @namespace.Base.extend(@namespace, 'Extended') - instance = new @namespace.Extended first, second, third - expect(spy).to.have.been.calledWithExactly first, second, third - expect(spy).to.have.been.calledOn instance - - it 'allows to extend the prototype', -> - First = SpaceObject.extend first: 1, get: (property) -> @[property] - Second = First.extend second: 2, get: -> First::get.apply this, arguments - class Third extends Second - get: (property) -> super property - instance = new Third() - expect(instance.get('first')).to.equal 1 - expect(instance.get('second')).to.equal 2 - - describe "providing fully qualified class path", -> - - it "registers the class for internal lookup", -> - Space.namespace('My.custom') - FirstClass = SpaceObject.extend('My.custom.FirstClass', {}) - SecondClass = SpaceObject.extend('My.custom.SecondClass', {}) - expect(Space.resolvePath 'My.custom.FirstClass').to.equal(FirstClass) - expect(Space.resolvePath 'My.custom.SecondClass').to.equal(SecondClass) - - it "assigns the class path", -> - className = 'My.custom.Class' - MyClass = SpaceObject.extend(className) - expect(MyClass.toString()).to.equal(className) - expect(new MyClass().toString()).to.equal(className) - - it "exposes the class on the global scope if possible", -> - my = {} - my.namespace = Space.namespace('my.namespace') - MyClass = SpaceObject.extend('my.namespace.MyClass') - expect(my.namespace.MyClass).to.equal(MyClass) - - it "works correctly without nested namespaces", -> - MyClass = SpaceObject.extend('MyClass') - expect(Space.resolvePath 'MyClass').to.equal(MyClass) - - describe "working with static class properties", -> - - it 'allows you to define static class properties', -> - myStatics = {} - MyClass = SpaceObject.extend statics: { myStatics: myStatics } - expect(MyClass.myStatics).to.equal(myStatics) - - it 'provides an api for defining a callback while extending', -> - onExtendingSpy = sinon.spy() - MyClass = SpaceObject.extend onExtending: onExtendingSpy - expect(onExtendingSpy).to.have.been.calledOn(MyClass) - - describe 'creating instances', -> - - it 'creates a new instance of given class', -> - expect(SpaceObject.create()).to.be.instanceof SpaceObject - - it 'allows to initialize the instance with given properties', -> - instance = SpaceObject.create first: 1, get: (property) -> @[property] - expect(instance.get 'first').to.equal 1 - - it 'forwards any number of arguments to the constructor', -> - Base = SpaceObject.extend Constructor: (@first, @second) -> - instance = Base.create 1, 2 - expect(instance.first).to.equal 1 - expect(instance.second).to.equal 2 - - describe "inheritance helpers", -> - - Base = SpaceObject.extend { - statics: { prop: 'static', method: -> } - prop: 'prototype' - method: -> - } - Sub = Base.extend() - GrandSub = Sub.extend() - - describe "static", -> - - it "can tell if there is a super class", -> - expect(Sub.hasSuperClass()).to.be.true - - it "can return the super class", -> - expect(Sub.superClass()).to.equal(Base) - - it "returns undefined if there is no super class", -> - expect(SpaceObject.superClass()).to.equal(undefined) - - it "can return a static prop or method of the super class", -> - expect(Sub.superClass('prop')).to.equal(Base.prop) - expect(Sub.superClass('method')).to.equal(Base.method) - - it "can give back a flat array of sub classes", -> - expect(Base.subClasses()).to.eql [Sub, GrandSub] - expect(Sub.subClasses()).to.eql [GrandSub] - expect(GrandSub.subClasses()).to.eql [] - - describe "prototype", -> - - it "can tell if there is a super class", -> - expect(new Sub().hasSuperClass()).to.be.true - - it "can return the super class", -> - expect(new Sub().superClass()).to.equal(Base) - - it "can return a static prop or method of the super class", -> - expect(new Sub().superClass('prop')).to.equal(Base::prop) - expect(new Sub().superClass('method')).to.equal(Base::method) - - describe 'mixins', -> - - it 'adds methods to the prototype', -> - testMixin = test: -> - TestClass = SpaceObject.extend() - TestClass.mixin testMixin - expect(TestClass::test).to.equal testMixin.test - - it 'overrides existing methods of the prototype', -> - testMixin = test: -> - TestClass = SpaceObject.extend test: -> - TestClass.mixin testMixin - expect(TestClass::test).to.equal testMixin.test - - it 'merges object properties', -> - testMixin = dependencies: second: 'second' - TestClass = SpaceObject.extend dependencies: first: 'first' - TestClass.mixin testMixin - expect(TestClass::dependencies.first).to.equal 'first' - expect(TestClass::dependencies.second).to.equal 'second' - - it "does not modify other mixins when merging properties", -> - FirstMixin = dependencies: firstMixin: 'onExtending' - FirstClass = SpaceObject.extend { - mixin: [FirstMixin] - dependencies: first: 'first' - } - FirstClass.mixin dependencies: firstMixin: 'afterExtending' - expect(FirstMixin).toMatch dependencies: firstMixin: 'onExtending' - expect(FirstClass.prototype.dependencies).toMatch { - first: 'first' - firstMixin: 'afterExtending' - } - - it "can provide a hook that is called when the mixin is applied", -> - myMixin = onMixinApplied: sinon.spy() - TestClass = SpaceObject.extend() - TestClass.mixin myMixin - expect(myMixin.onMixinApplied).to.have.been.calledOnce - - it 'can be defined as prototype property when extending classes', -> - myMixin = { onMixinApplied: sinon.spy() } - MyClass = SpaceObject.extend mixin: [myMixin] - expect(myMixin.onMixinApplied).to.have.been.calledOn(MyClass) - - it 'can be used to mixin static properties on to the class', -> - myMixin = statics: { myMethod: sinon.spy() } - MyClass = SpaceObject.extend mixin: [myMixin] - MyClass.myMethod() - expect(myMixin.statics.myMethod).to.have.been.calledOn(MyClass) - - it 'can be checked which mixins a class has', -> - FirstMixin = {} - SecondMixin = {} - ThirdMixin = {} - MyClass = SpaceObject.extend({ mixin: FirstMixin }) - MyClass.mixin(SecondMixin) - instance = new MyClass() - # Static checks - expect(MyClass.hasMixin(FirstMixin)).to.be.true - expect(MyClass.hasMixin(SecondMixin)).to.be.true - expect(MyClass.hasMixin(ThirdMixin)).to.be.false - # Instance checks - expect(instance.hasMixin(FirstMixin)).to.be.true - expect(instance.hasMixin(SecondMixin)).to.be.true - expect(instance.hasMixin(ThirdMixin)).to.be.false - - describe "mixin inheritance", -> - - it "does not apply mixins to super classes", -> - firstMixin = {} - secondMixin = {} - SuperClass = SpaceObject.extend mixin: firstMixin - SubClass = SuperClass.extend mixin: secondMixin - expect(SuperClass.hasMixin(firstMixin)).to.be.true - expect(SuperClass.hasMixin(secondMixin)).to.be.false - expect(SubClass.hasMixin(firstMixin)).to.be.true - expect(SubClass.hasMixin(secondMixin)).to.be.true - - it "inherits mixins to children when added to base class later on", -> - LateMixin = { statics: { test: 'property' } } - # Base class with a mixin - BaseClass = SpaceObject.extend() - # Sublcass with its own mixin - SubClass = BaseClass.extend() - # Later we extend base class - BaseClass.mixin LateMixin - # Sub class should have all three mixins correctly applied - expect(SubClass.hasMixin(LateMixin)).to.be.true - expect(SubClass.test).to.equal LateMixin.statics.test - - describe "onDependenciesReady hooks", -> - - it "can provide a hook that is called when dependencies of host class are ready", -> - myMixin = onDependenciesReady: sinon.spy() - TestClass = SpaceObject.extend() - TestClass.mixin myMixin - new TestClass().onDependenciesReady() - expect(myMixin.onDependenciesReady).to.have.been.calledOnce - - it "inherits the onDependenciesReady hooks to sub classes", -> - firstMixin = onDependenciesReady: sinon.spy() - secondMixin = onDependenciesReady: sinon.spy() - SuperClass = SpaceObject.extend() - SuperClass.mixin firstMixin - SubClass = SuperClass.extend() - SubClass.mixin secondMixin - new SubClass().onDependenciesReady() - expect(firstMixin.onDependenciesReady).to.have.been.calledOnce - expect(secondMixin.onDependenciesReady).to.have.been.calledOnce - - it "calls inherited mixin hooks only once per chain", -> - myMixin = onDependenciesReady: sinon.spy() - SuperClass = SpaceObject.extend() - SuperClass.mixin myMixin - SubClass = SuperClass.extend() - new SubClass().onDependenciesReady() - expect(myMixin.onDependenciesReady).to.have.been.calledOnce - - describe "construction hooks", -> - - it "can provide a hook that is called on construction of host class", -> - myMixin = onConstruction: sinon.spy() - TestClass = SpaceObject.extend() - TestClass.mixin myMixin - first = {} - second = {} - new TestClass(first, second) - expect(myMixin.onConstruction).to.have.been.calledWithExactly(first, second) - diff --git a/tests/unit/object.unit.js b/tests/unit/object.unit.js new file mode 100644 index 0000000..504b635 --- /dev/null +++ b/tests/unit/object.unit.js @@ -0,0 +1,354 @@ +import SpaceObject from '../../source/object.coffee'; +import Space from '../../source/space.js'; + +describe('SpaceObject', function() { + + beforeEach(() => { + this.namespace = {}; + }); + + describe('extending', () => { + + it('creates and returns a subclass', () => { + SpaceObject.extend(this.namespace, 'MyClass'); + expect(this.namespace.MyClass).to.extend(SpaceObject); + }); + + it('applies the arguments to the super constructor', () => { + [first, second, third] = ['first', 2, {}]; + const spy = sinon.spy(); + SpaceObject.extend(this.namespace, 'Base', { + Constructor() {spy.apply(this, arguments);} + }); + this.namespace.Base.extend(this.namespace, 'Extended'); + const instance = new this.namespace.Extended(first, second, third); + expect(spy).to.have.been.calledWithExactly(first, second, third); + expect(spy).to.have.been.calledOn(instance); + }); + + it('allows to extend the prototype', () => { + const First = SpaceObject.extend({ + first: 1, + get: function(property) { + return this[property]; + } + }); + + const Second = First.extend({ + second: 2, + get: function() { + return First.prototype.get.apply(this, arguments); + } + }); + + class Third extends Second { + get(property) { + return super.get(property); + } + } + + const instance = new Third(); + expect(instance.get('first')).to.equal(1); + expect(instance.get('second')).to.equal(2); + }); + + describe("providing fully qualified class path", () => { + + it("registers the class for internal lookup", () => { + Space.namespace('My.custom'); + const FirstClass = SpaceObject.extend('My.custom.FirstClass', {}); + const SecondClass = SpaceObject.extend('My.custom.SecondClass', {}); + expect(Space.resolvePath('My.custom.FirstClass')).to.equal(FirstClass); + expect(Space.resolvePath('My.custom.SecondClass')).to.equal(SecondClass); + }); + + it("assigns the class path", () => { + const className = 'My.custom.Class'; + MyClass = SpaceObject.extend(className); + expect(MyClass.toString()).to.equal(className); + expect(new MyClass().toString()).to.equal(className); + }); + + it("exposes the class on the global scope if possible", () => { + const my = {}; + my.namespace = Space.namespace('my.namespace'); + MyClass = SpaceObject.extend('my.namespace.MyClass'); + expect(my.namespace.MyClass).to.equal(MyClass); + }); + + it("works correctly without nested namespaces", () => { + const MyClass = SpaceObject.extend('MyClass'); + expect(Space.resolvePath('MyClass')).to.equal(MyClass); + }); + }); + + describe("working with static class properties", () => { + + it('allows you to define static class properties', () => { + const myStatics = {}; + MyClass = SpaceObject.extend({statics: {myStatics: myStatics}}); + expect(MyClass.myStatics).to.equal(myStatics); + }); + + it('provides an api for defining a callback while extending', () => { + const onExtendingSpy = sinon.spy(); + MyClass = SpaceObject.extend({onExtending: onExtendingSpy}); + expect(onExtendingSpy).to.have.been.calledOn(MyClass); + }); + }); + }); + + describe('creating instances', () => { + it('creates a new instance of given class', () => { + expect(SpaceObject.create()).to.be.instanceof(SpaceObject); + }); + + it('allows to initialize the instance with given properties', () => { + const instance = SpaceObject.create({ + first: 1, + get(property) {return this[property];} + }); + expect(instance.get('first')).to.equal(1); + }); + + it('forwards any number of arguments to the constructor', () => { + const Base = SpaceObject.extend({ + Constructor: function(first, second) { + this.first = first; + this.second = second; + } + }); + const instance = Base.create(1, 2); + expect(instance.first).to.equal(1); + expect(instance.second).to.equal(2); + }); + }); + + describe("inheritance helpers", () => { + const Base = SpaceObject.extend({ + statics: { + prop: 'static', + method: function() {} + }, + prop: 'prototype', + method: function() {} + }); + const Sub = Base.extend(); + const GrandSub = Sub.extend(); + + describe("static", () => { + + it("can tell if there is a super class", () => { + expect(Sub.hasSuperClass()).to.be.true; + }); + + it("can return the super class", () => { + expect(Sub.superClass()).to.equal(Base); + }); + + it("returns undefined if there is no super class", () => { + expect(SpaceObject.superClass()).to.equal(undefined); + }); + + it("can return a static prop or method of the super class", () => { + expect(Sub.superClass('prop')).to.equal(Base.prop); + expect(Sub.superClass('method')).to.equal(Base.method); + }); + + it("can give back a flat array of sub classes", () => { + expect(Base.subClasses()).to.eql([Sub, GrandSub]); + expect(Sub.subClasses()).to.eql([GrandSub]); + expect(GrandSub.subClasses()).to.eql([]); + }); + }); + + describe("prototype", () => { + it("can tell if there is a super class", () => { + expect(new Sub().hasSuperClass()).to.be.true; + }); + + it("can return the super class", () => { + expect(new Sub().superClass()).to.equal(Base); + }); + + it("can return a static prop or method of the super class", () => { + expect(new Sub().superClass('prop')).to.equal(Base.prototype.prop); + expect(new Sub().superClass('method')).to.equal(Base.prototype.method); + }); + }); + }); + + describe('mixins', () => { + + it('adds methods to the prototype', () => { + const testMixin = { + test() {} + }; + const TestClass = SpaceObject.extend(); + TestClass.mixin(testMixin); + expect(TestClass.prototype.test).to.equal(testMixin.test); + }); + + it('overrides existing methods of the prototype', () => { + const testMixin = { + test: function() {} + }; + const TestClass = SpaceObject.extend({ + test: function() {} + }); + TestClass.mixin(testMixin); + expect(TestClass.prototype.test).to.equal(testMixin.test); + }); + + it('merges object properties', () => { + const testMixin = { + dependencies: { + second: 'second' + } + }; + + const TestClass = SpaceObject.extend({ + dependencies: { + first: 'first' + } + }); + TestClass.mixin(testMixin); + expect(TestClass.prototype.dependencies.first).to.equal('first'); + expect(TestClass.prototype.dependencies.second).to.equal('second'); + }); + + it("does not modify other mixins when merging properties", () => { + const FirstMixin = { + dependencies: { + firstMixin: 'onExtending' + } + }; + const FirstClass = SpaceObject.extend({ + mixin: [FirstMixin], + dependencies: { + first: 'first' + } + }); + FirstClass.mixin({ + dependencies: { + firstMixin: 'afterExtending' + } + }); + expect(FirstMixin).toMatch({dependencies: {firstMixin: 'onExtending'}}); + expect(FirstClass.prototype.dependencies).toMatch({ + first: 'first', + firstMixin: 'afterExtending' + }); + }); + + it("can provide a hook that is called when the mixin is applied", () => { + const myMixin = {onMixinApplied: sinon.spy()}; + const TestClass = SpaceObject.extend(); + TestClass.mixin(myMixin); + expect(myMixin.onMixinApplied).to.have.been.calledOnce; + }); + + it('can be defined as prototype property when extending classes', () => { + const myMixin = { onMixinApplied: sinon.spy() }; + const MyClass = SpaceObject.extend({mixin: [myMixin]}); + expect(myMixin.onMixinApplied).to.have.been.calledOn(MyClass); + }); + + it('can be used to mixin static properties on to the class', () => { + const myMixin = {statics: { myMethod: sinon.spy() }}; + const MyClass = SpaceObject.extend({mixin: [myMixin]}); + MyClass.myMethod(); + expect(myMixin.statics.myMethod).to.have.been.calledOn(MyClass); + }); + + it('can be checked which mixins a class has', () => { + const FirstMixin = {}; + const SecondMixin = {}; + const ThirdMixin = {}; + + const MyClass = SpaceObject.extend({mixin: FirstMixin}); + MyClass.mixin(SecondMixin); + const instance = new MyClass(); + // Static checks + expect(MyClass.hasMixin(FirstMixin)).to.be.true; + expect(MyClass.hasMixin(SecondMixin)).to.be.true; + expect(MyClass.hasMixin(ThirdMixin)).to.be.false; + // Instance checks + expect(instance.hasMixin(FirstMixin)).to.be.true; + expect(instance.hasMixin(SecondMixin)).to.be.true; + expect(instance.hasMixin(ThirdMixin)).to.be.false; + }); + + describe("mixin inheritance", () => { + + it("does not apply mixins to super classes", () => { + const firstMixin = {}; + const secondMixin = {}; + const SuperClass = SpaceObject.extend({mixin: firstMixin}); + const SubClass = SuperClass.extend({mixin: secondMixin}); + expect(SuperClass.hasMixin(firstMixin)).to.be.true; + expect(SuperClass.hasMixin(secondMixin)).to.be.false; + expect(SubClass.hasMixin(firstMixin)).to.be.true; + expect(SubClass.hasMixin(secondMixin)).to.be.true; + }); + + it("inherits mixins to children when added to base class later on", () => { + const LateMixin = { statics: { test: 'property' } }; + // Base class with a mixin + const BaseClass = SpaceObject.extend(); + // Sublcass with its own mixin + const SubClass = BaseClass.extend(); + // Later we extend base class + BaseClass.mixin(LateMixin); + // Sub class should have all three mixins correctly applied + expect(SubClass.hasMixin(LateMixin)).to.be.true; + expect(SubClass.test).to.equal(LateMixin.statics.test); + }); + }); + + describe("onDependenciesReady hooks", () => { + + it("can provide a hook that is called when dependencies of host class are ready", () => { + const myMixin = {onDependenciesReady: sinon.spy()}; + const TestClass = SpaceObject.extend(); + TestClass.mixin(myMixin); + new TestClass().onDependenciesReady(); + expect(myMixin.onDependenciesReady).to.have.been.calledOnce; + }); + + it("inherits the onDependenciesReady hooks to sub classes", () => { + const firstMixin = {onDependenciesReady: sinon.spy()}; + const secondMixin = {onDependenciesReady: sinon.spy()}; + const SuperClass = SpaceObject.extend(); + SuperClass.mixin(firstMixin); + const SubClass = SuperClass.extend(); + SubClass.mixin(secondMixin); + new SubClass().onDependenciesReady(); + expect(firstMixin.onDependenciesReady).to.have.been.calledOnce; + expect(secondMixin.onDependenciesReady).to.have.been.calledOnce; + }); + + it("calls inherited mixin hooks only once per chain", () => { + const myMixin = {onDependenciesReady: sinon.spy()}; + const SuperClass = SpaceObject.extend(); + SuperClass.mixin(myMixin); + const SubClass = SuperClass.extend(); + new SubClass().onDependenciesReady(); + expect(myMixin.onDependenciesReady).to.have.been.calledOnce; + }); + }); + + describe("construction hooks", () => { + + it("can provide a hook that is called on construction of host class", () => { + const myMixin = {onConstruction: sinon.spy()}; + const TestClass = SpaceObject.extend(); + TestClass.mixin(myMixin); + const first = {}; + const second = {}; + new TestClass(first, second); + expect(myMixin.onConstruction).to.have.been.calledWithExactly(first, second); + }); + }); + }); +}); From 450580dc1df5310948d6aaa17fb011a0c8fe741f Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:11:23 +0100 Subject: [PATCH 054/111] Converts Module unit test --- tests/unit/module.unit.coffee | 169 ---------------------------- tests/unit/module.unit.js | 206 ++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 169 deletions(-) delete mode 100644 tests/unit/module.unit.coffee create mode 100644 tests/unit/module.unit.js diff --git a/tests/unit/module.unit.coffee b/tests/unit/module.unit.coffee deleted file mode 100644 index eb9d70a..0000000 --- a/tests/unit/module.unit.coffee +++ /dev/null @@ -1,169 +0,0 @@ -import SpaceObject from '../../source/object.coffee'; -import Module from '../../source/module.js'; -import {Injector} from '../../source/injector.js'; - -describe 'Module', -> - - beforeEach -> - # Reset published space modules - Module.published = {} - - it 'extends space object', -> expect(Module).to.extend SpaceObject - - describe '@publish', -> - - it 'adds given module to the static collection of published modules', -> - module = Module.define 'test' - expect(Module.published['test']).to.equal module - - it 'throws an error if two modules try to publish under same name', -> - publishTwoModulesWithSameName = -> - Module.define 'test' - Module.define 'test' - expect(publishTwoModulesWithSameName).to.throw Error - - describe '@require', -> - - it 'returns published module for given identifier', -> - module = Module.define 'test' - requiredModule = Module.require 'test' - expect(requiredModule).to.equal module - - it 'throws and error if no module was registered for given identifier', -> - requireUnkownModule = -> Module.require 'unknown module' - expect(requireUnkownModule).to.throw Error - - describe 'constructor', -> - - it 'sets required modules to empty array if none defined', -> - module = new Module() - expect(module.requiredModules).to.be.instanceof Array - expect(module.requiredModules).to.be.empty - - it 'leaves the defined required modules intact', -> - testArray = [] - module = Module.create requiredModules: testArray - expect(module.requiredModules).to.equal testArray - - it 'sets the correct state', -> - module = new Module() - expect(module.is 'constructed').to.be.true - - -describe 'Module - #initialize', -> - - beforeEach -> - # Reset published space modules - Module.published = {} - @injector = new Injector() - sinon.spy @injector, 'injectInto' - @module = new Module() - # faked required modules to spy on - @SubModule1 = Module.define 'SubModule1' - @SubModule2 = Module.define 'SubModule2' - @app = modules: {} - - it 'asks the injector to inject dependencies into the module', -> - @module.initialize @app, @injector - expect(@injector.injectInto).to.have.been.calledWith @module - - it 'throws an error if no injector is provided', -> - initializeWithoutInjector = => @module.initialize() - expect(initializeWithoutInjector).to.throw Error - - it 'sets the initialized flag correctly', -> - @module.initialize @app, @injector - expect(@module.is 'initialized').to.be.true - - it.server 'adds Npm as property to the module', -> - @module.initialize @app, @injector - expect(@module.npm.require).to.be.defined - - it 'invokes the onInitialize method on itself', -> - @module.onInitialize = sinon.spy() - @module.initialize @app, @injector - expect(@module.onInitialize).to.have.been.calledOnce - - it 'creates required modules and adds them to the app', -> - @module.requiredModules = [@SubModule1.name, @SubModule2.name] - @module.initialize @app, @injector - expect(@app.modules[@SubModule1.name]).to.be.instanceof(@SubModule1) - expect(@app.modules[@SubModule2.name]).to.be.instanceof(@SubModule2) - - it 'initializes required modules', -> - sinon.stub @SubModule1.prototype, 'initialize' - @module.requiredModules = [@SubModule1.name] - @module.initialize @app, @injector - expect(@SubModule1.prototype.initialize).to.have.been.calledOnce - - it 'can only be initialized once', -> - @module.onInitialize = sinon.spy() - @module.initialize @app, @injector - @module.initialize @app, @injector - expect(@module.onInitialize).to.have.been.calledOnce - -describe 'Module - #start', -> - - beforeEach -> - @module = new Module() - @module.log = {debug: sinon.spy()} - @module.start() - @module._runLifeCycleAction = sinon.spy() - - it 'sets the state to running', -> - expect(@module.is 'running').to.be.true - - it 'ignores start calls on a running module', -> - @module.start() - expect(@module._runLifeCycleAction).not.to.have.been.called - -describe 'Module - #stop', -> - - beforeEach -> - @module = new Module() - @module.log = {debug: sinon.spy()} - @module.start() - @module.stop() - @module._runLifeCycleAction = sinon.spy() - - it 'sets the state to stopped', -> - expect(@module.is 'stopped').to.be.true - - it 'ignores stop calls on a stopped module', -> - @module.stop() - expect(@module._runLifeCycleAction).not.to.have.been.called - -describe 'Module - #reset', -> - - beforeEach -> - @module = new Module() - @module.log = {debug: sinon.spy()} - @module._runLifeCycleAction = sinon.spy() - - it.server 'rejects attempts to reset when in production', -> - nodeEnvBackup = process.env.NODE_ENV - process.env.NODE_ENV = 'production' - @module.reset() - process.env.NODE_ENV = nodeEnvBackup - expect(@module._runLifeCycleAction).not.to.have.been.called - -describe "Module - wrappable lifecycle hooks", -> - - it "allows mixins to hook into the module lifecycle", -> - moduleOnInitializeSpy = sinon.spy() - mixinOnInitializeSpy = sinon.spy() - MyModule = Module.extend { - onInitialize: moduleOnInitializeSpy - } - MyModule.mixin { - onDependenciesReady: -> - @_wrapLifecycleHook 'onInitialize', (onInitialize) -> - onInitialize.call(this) - mixinOnInitializeSpy.call(this) - } - module = new MyModule() - module.initialize(module, new Injector()) - - expect(moduleOnInitializeSpy).to.have.been.calledOnce - expect(mixinOnInitializeSpy).to.have.been.calledOnce - expect(moduleOnInitializeSpy).to.have.been.calledBefore(mixinOnInitializeSpy) diff --git a/tests/unit/module.unit.js b/tests/unit/module.unit.js new file mode 100644 index 0000000..aaddfb5 --- /dev/null +++ b/tests/unit/module.unit.js @@ -0,0 +1,206 @@ +import SpaceObject from '../../source/object.coffee'; +import Module from '../../source/module.js'; +import {Injector} from '../../source/injector.js'; + +describe('Module', function() { + + beforeEach(() => { + // Reset published space modules + Module.published = {}; + }); + + it('extends space object', () => { + expect(Module).to.extend(SpaceObject); + }); + + describe('static publish', () => { + + it('adds given module to the static collection of published modules', () => { + const module = Module.define('test'); + expect(Module.published.test).to.equal(module); + }); + + it('throws an error if two modules try to publish under same name', () => { + const publishTwoModulesWithSameName = () => { + Module.define('test'); + Module.define('test'); + }; + expect(publishTwoModulesWithSameName).to.throw(Error); + }); + }); + + describe('static require', () => { + it('returns published module for given identifier', () => { + const module = Module.define('test'); + requiredModule = Module.require('test'); + expect(requiredModule).to.equal(module); + }); + + it('throws and error if no module was registered for given identifier', () => { + const requireUnkownModule = () => {Module.require('unknown module');}; + expect(requireUnkownModule).to.throw(Error); + }); + }); + + describe('constructor', () => { + it('sets required modules to empty array if none defined', () => { + const module = new Module(); + expect(module.requiredModules).to.be.instanceof(Array); + expect(module.requiredModules).to.be.empty; + }); + + it('leaves the defined required modules intact', () => { + const testArray = []; + const module = Module.create({requiredModules: testArray}); + expect(module.requiredModules).to.equal(testArray); + }); + + it('sets the correct state', () => { + const module = new Module(); + expect(module.is('constructed')).to.be.true; + }); + }); +}); + +describe('Module - #initialize', () => { + + beforeEach(() => { + // Reset published space modules + Module.published = {}; + this.injector = new Injector(); + sinon.spy(this.injector, 'injectInto'); + this.module = new Module(); + // faked required modules to spy on + this.SubModule1 = Module.define('SubModule1'); + this.SubModule2 = Module.define('SubModule2'); + this.app = {modules: {}}; + }); + + it('asks the injector to inject dependencies into the module', () => { + this.module.initialize(this.app, this.injector); + expect(this.injector.injectInto).to.have.been.calledWith(this.module); + }); + + it('throws an error if no injector is provided', () => { + const initializeWithoutInjector = () => this.module.initialize(); + expect(initializeWithoutInjector).to.throw(Error); + }); + + it('sets the initialized flag correctly', () => { + this.module.initialize(this.app, this.injector); + expect(this.module.is('initialized')).to.be.true; + }); + + it.server('server adds Npm as property to the module', () => { + this.module.initialize(this.app, this.injector); + expect(this.module.npm.require).to.be.defined; + }); + + it('invokes the onInitialize method on itself', () => { + this.module.onInitialize = sinon.spy(); + this.module.initialize(this.app, this.injector); + expect(this.module.onInitialize).to.have.been.calledOnce; + }); + + it('creates required modules and adds them to the app', () => { + this.module.requiredModules = [this.SubModule1.name, this.SubModule2.name]; + this.module.initialize(this.app, this.injector); + expect(this.app.modules[this.SubModule1.name]).to.be.instanceof(this.SubModule1); + expect(this.app.modules[this.SubModule2.name]).to.be.instanceof(this.SubModule2); + }); + + it('initializes required modules', () => { + sinon.stub(this.SubModule1.prototype, 'initialize'); + this.module.requiredModules = [this.SubModule1.name]; + this.module.initialize(this.app, this.injector); + expect(this.SubModule1.prototype.initialize).to.have.been.calledOnce; + }); + + it('can only be initialized once', () => { + this.module.onInitialize = sinon.spy(); + this.module.initialize(this.app, this.injector); + this.module.initialize(this.app, this.injector); + expect(this.module.onInitialize).to.have.been.calledOnce; + }); +}); + +describe('Module - #start', () => { + + beforeEach(() => { + this.module = new Module(); + this.module.log = {debug: sinon.spy()}; + this.module.start(); + this.module._runLifeCycleAction = sinon.spy(); + }); + + it('sets the state to running', () => { + expect(this.module.is('running')).to.be.true; + }); + + it('ignores start calls on a running module', () => { + this.module.start(); + expect(this.module._runLifeCycleAction).not.to.have.been.called; + }); +}); + +describe('Module - #stop', () => { + + beforeEach(() => { + this.module = new Module(); + this.module.log = {debug: sinon.spy()}; + this.module.start(); + this.module.stop(); + this.module._runLifeCycleAction = sinon.spy(); + }); + + it('sets the state to stopped', () => { + expect(this.module.is('stopped')).to.be.true; + }); + + it('ignores stop calls on a stopped module', () => { + this.module.stop(); + expect(this.module._runLifeCycleAction).not.to.have.been.called; + }); +}); + +describe('Module - #reset', () => { + + beforeEach(() => { + this.module = new Module(); + this.module.log = {debug: sinon.spy()}; + this.module._runLifeCycleAction = sinon.spy(); + }); + + it.server('rejects attempts to reset when in production', () => { + const nodeEnvBackup = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + this.module.reset(); + process.env.NODE_ENV = nodeEnvBackup; + expect(this.module._runLifeCycleAction).not.to.have.been.called; + }); +}); + +describe("Module - wrappable lifecycle hooks", () => { + it("allows mixins to hook into the module lifecycle", () => { + const moduleOnInitializeSpy = sinon.spy(); + const mixinOnInitializeSpy = sinon.spy(); + MyModule = Module.extend({ + onInitialize: moduleOnInitializeSpy + }); + + MyModule.mixin({ + onDependenciesReady: function() { + this._wrapLifecycleHook('onInitialize', function(onInitialize) { + onInitialize.call(this); + return mixinOnInitializeSpy.call(this); + }); + } + }); + const module = new MyModule(); + module.initialize(module, new Injector()); + + expect(moduleOnInitializeSpy).to.have.been.calledOnce; + expect(mixinOnInitializeSpy).to.have.been.calledOnce; + expect(moduleOnInitializeSpy).to.have.been.calledBefore(mixinOnInitializeSpy); + }); +}); From 9ef2712325444c6cb89f9dfb0af031a29b2f9ad7 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:11:34 +0100 Subject: [PATCH 055/111] Converts Injector unit test --- tests/unit/injector.unit.coffee | 267 --------------------------- tests/unit/injector.unit.js | 317 ++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+), 267 deletions(-) delete mode 100644 tests/unit/injector.unit.coffee create mode 100644 tests/unit/injector.unit.js diff --git a/tests/unit/injector.unit.coffee b/tests/unit/injector.unit.coffee deleted file mode 100644 index eef088d..0000000 --- a/tests/unit/injector.unit.coffee +++ /dev/null @@ -1,267 +0,0 @@ -import {Injector} from '../../source/injector.js'; -import SpaceObject from '../../source/object.coffee'; -import Space from '../../source/space.js'; - -global = this - -describe 'Injector', -> - - beforeEach -> @injector = new Injector() - - # ============ MAPPINGS ============ # - - describe 'working with mappings', -> - - it 'injects into requested dependency', -> - myObject = dependencies: test: 'test' - testValue = {} - @injector.map('test').to testValue - @injector.map('myObject').to myObject - - expect(@injector.get('myObject').test).to.equal testValue - - it 'throws error if mapping doesnt exist', -> - id = 'blablub' - expect(=> @injector.get(id)).to.throw( - @injector.ERRORS.noMappingFound(id).message - ) - - it 'throws error if mapping would be overriden', -> - @injector.map('test').to 'test' - override = => @injector.map('test').to 'other' - expect(override).to.throw Error - - it 'can remove existing mappings', -> - @injector.map('test').to 'test' - @injector.remove 'test' - expect(=> @injector.get 'test').to.throw - - it 'provides an alias for getting values', -> - @injector.map('test').to 'test' - expect(@injector.create 'test').to.equal 'test' - - it 'uses the toString method if its not a string id', -> - class TestClass extends SpaceObject - @toString: -> 'TestClass' - - @injector.map(TestClass).asSingleton() - expect(@injector.get('TestClass')).to.be.instanceof TestClass - - it 'throws error if you try to map undefined', -> - expect(=> @injector.map(undefined)).to.throw @injector.ERRORS.cannotMapUndefinedId() - expect(=> @injector.map(null)).to.throw @injector.ERRORS.cannotMapUndefinedId() - - describe 'overriding mappings', -> - - it 'allows to override mappings', -> - @injector.map('test').to 'test' - @injector.override('test').to 'other' - expect(@injector.get('test')).to.equal 'other' - - it 'dynamically updates all dependent objects with the new dependency', -> - myObject = dependencies: test: 'test' - firstValue = { first: true } - secondValue = { second: true } - @injector.map('test').to firstValue - @injector.injectInto myObject - expect(myObject.test).to.equal firstValue - @injector.override('test').to secondValue - expect(myObject.test).to.equal secondValue - - it 'allows to de-register a dependent object from the mappings', -> - myObject = { - dependencies: - first: 'First' - second: 'Second' - } - firstValue = { first: true } - secondValue = { second: true } - @injector.map('First').to firstValue - @injector.map('Second').to secondValue - - @injector.injectInto myObject - firstMapping = @injector.getMappingFor 'First' - secondMapping = @injector.getMappingFor 'Second' - expect(firstMapping.hasDependent(myObject)).to.be.true - expect(secondMapping.hasDependent(myObject)).to.be.true - # Release the reference to the dependent - @injector.release(myObject) - expect(firstMapping.hasDependent(myObject)).to.be.false - expect(secondMapping.hasDependent(myObject)).to.be.false - - it 'tells the dependent object when a dependency changed', -> - dependentObject = { - dependencies: { - test: 'Test' - } - onDependencyChanged: sinon.spy() - } - firstValue = {} - secondValue = {} - @injector.map('Test').to firstValue - @injector.injectInto dependentObject - @injector.override('Test').to secondValue - - expect(dependentObject.onDependencyChanged).to.have.been.calledWith( - 'test', secondValue - ) - - # ========== INJECTING DEPENDENCIES ========= # - - describe 'injecting dependencies', -> - - it 'injects static values', -> - value = {} - @injector.map('test').to value - instance = SpaceObject.create dependencies: value: 'test' - @injector.injectInto instance - expect(instance.value).to.equal value - - it 'injects into provided dependencies', -> - first = dependencies: value: 'test' - second = dependencies: first: 'first' - @injector.map('test').to 'value' - @injector.map('first').to first - - @injector.injectInto second - expect(second.first).to.equal first - expect(first.value).to.equal 'value' - - it 'handles inherited dependencies', -> - Base = SpaceObject.extend dependencies: base: 'base' - Extended = Base.extend dependencies: extended: 'extended' - @injector.map('base').to 'base' - @injector.map('extended').to 'extended' - - instance = new Extended() - @injector.injectInto instance - expect(instance.base).to.equal 'base' - expect(instance.extended).to.equal 'extended' - - it 'never overrides existing properties', -> - instance = SpaceObject.create - dependencies: test: 'test' - test: 'value' - - @injector.map('test').to('test') - @injector.injectInto instance - - expect(instance.test).to.equal 'value' - - describe 'when dependencies are ready', -> - - it 'tells the instance that they are ready', -> - value = 'test' - instance = SpaceObject.create - dependencies: value: 'value' - onDependenciesReady: sinon.spy() - - @injector.map('value').to('value') - @injector.injectInto instance - @injector.injectInto instance # shouldnt trigger twice - - expect(instance.onDependenciesReady).to.have.been.calledOnce - - it 'tells every single instance exactly once', -> - readySpy = sinon.spy() - class TestClass extends SpaceObject - dependencies: value: 'test' - onDependenciesReady: readySpy - - @injector.map('test').to 'test' - @injector.map('TestClass').toInstancesOf TestClass - - first = @injector.create 'TestClass' - second = @injector.create 'TestClass' - - expect(readySpy).to.have.been.calledTwice - expect(readySpy).to.have.been.calledOn first - expect(readySpy).to.have.been.calledOn second - - # ============ DEFAULT PROVIDERS ============ # - - describe 'default providers', -> - - describe 'static value providers', -> - - it 'maps to static value', -> - value = 'test' - @injector.map('first').to value - @injector.map('second').toStaticValue value - - expect(@injector.get('first')).to.equal value - expect(@injector.get('second')).to.equal value - - it 'supports global namespace lookup', -> - global.Space.__test__ = TestClass: SpaceObject.extend() - path = 'Space.__test__.TestClass' - @injector.map(path).asStaticValue() - - expect(@injector.get(path)).to.equal Space.__test__.TestClass - delete global.Space.__test__ - - it 'can uses static toString method if available', -> - class Test - @toString: -> 'Test' - - @injector.map(Test).asStaticValue() - expect(@injector.get('Test')).to.equal Test - - describe 'instance provider', -> - - it 'creates new instances for each request', -> - class Test - @injector.map('Test').toClass Test - - first = @injector.get 'Test' - second = @injector.get 'Test' - - expect(first).to.be.instanceof Test - expect(second).to.be.instanceof Test - expect(first).not.to.equal second - - describe 'singleton provider', -> - - it 'maps class as singleton', -> - class Test - @toString: -> 'Test' - @injector.map(Test).asSingleton() - first = @injector.get('Test') - second = @injector.get('Test') - - expect(first).to.be.instanceof Test - expect(first).to.equal second - - it 'maps id to singleton of class', -> - class Test - @injector.map('Test').toSingleton Test - first = @injector.get('Test') - second = @injector.get('Test') - - expect(first).to.be.instanceof Test - expect(first).to.equal second - - it 'looks up the value on global namespace if only a path is given', -> - global.Space.__test__ = TestClass: SpaceObject.extend() - @injector.map('Space.__test__.TestClass').asSingleton() - - first = @injector.get('Space.__test__.TestClass') - second = @injector.get('Space.__test__.TestClass') - - expect(first).to.be.instanceof Space.__test__.TestClass - expect(first).to.equal second - delete global.Space.__test__ - - # ============ CUSTOM PROVIDERS ============ # - - describe 'adding custom providers', -> - - it 'adds the provider to the api', -> - - loremIpsum = 'lorem ipsum' - - @injector.addProvider 'toLoremIpsum', SpaceObject.extend - Constructor: -> @provide = -> loremIpsum - - @injector.map('test').toLoremIpsum() - expect(@injector.get 'test').to.equal loremIpsum diff --git a/tests/unit/injector.unit.js b/tests/unit/injector.unit.js new file mode 100644 index 0000000..c9a464a --- /dev/null +++ b/tests/unit/injector.unit.js @@ -0,0 +1,317 @@ +import {Injector} from '../../source/injector.js'; +import SpaceObject from '../../source/object.coffee'; +import Space from '../../source/space.js'; + +describe('Injector', function() { + + beforeEach(() => { + this.injector = new Injector(); + }); + + // ============ MAPPINGS ============ + + describe('working with mappings', () => { + + it('injects into requested dependency', () => { + const myObject = {dependencies: {test: 'test'}}; + const testValue = {}; + this.injector.map('test').to(testValue); + this.injector.map('myObject').to(myObject); + + expect(this.injector.get('myObject').test).to.equal(testValue); + }); + + it(`throws error if mapping doesn't exist`, () => { + const id = 'blablub'; + expect(()=> this.injector.get(id)).to.throw( + this.injector.ERRORS.noMappingFound(id).message + ); + }); + + it('throws error if mapping would be overriden', () => { + this.injector.map('test').to('test'); + const override = () => this.injector.map('test').to('other'); + expect(override).to.throw(Error); + }); + + it('can remove existing mappings', () => { + this.injector.map('test').to('test'); + this.injector.remove('test'); + expect(()=> this.injector.get('test')).to.throw(Error); + }); + + it('provides an alias for getting values', () => { + this.injector.map('test').to('test'); + expect(this.injector.create('test')).to.equal('test'); + }); + + it('uses the toString method if its not a string id', () => { + class TestClass extends SpaceObject { + static toString() {return 'TestClass';} + } + + this.injector.map(TestClass).asSingleton(); + expect(this.injector.get('TestClass')).to.be.instanceof(TestClass); + }); + + it('throws error if you try to map undefined', () => { + expect(()=> this.injector.map(undefined)).to.throw( + this.injector.ERRORS.cannotMapUndefinedId() + ); + expect(()=> this.injector.map(null)).to.throw( + this.injector.ERRORS.cannotMapUndefinedId() + ); + }); + }); + + describe('overriding mappings', () => { + it('allows to override mappings', () => { + this.injector.map('test').to('test'); + this.injector.override('test').to('other'); + expect(this.injector.get('test')).to.equal('other'); + }); + + it('dynamically updates all dependent objects with the new dependency', () => { + const myObject = {dependencies: {test: 'test'}}; + const firstValue = {first: true}; + const secondValue = {second: true}; + this.injector.map('test').to(firstValue); + this.injector.injectInto(myObject); + expect(myObject.test).to.equal(firstValue); + this.injector.override('test').to(secondValue); + expect(myObject.test).to.equal(secondValue); + }); + + it('allows to de-register a dependent object from the mappings', () => { + const myObject = { + dependencies: { + first: 'First', + second: 'Second' + } + }; + const firstValue = { first: true }; + const secondValue = { second: true }; + this.injector.map('First').to(firstValue); + this.injector.map('Second').to(secondValue); + + this.injector.injectInto(myObject); + const firstMapping = this.injector.getMappingFor('First'); + const secondMapping = this.injector.getMappingFor('Second'); + expect(firstMapping.hasDependent(myObject)).to.be.true; + expect(secondMapping.hasDependent(myObject)).to.be.true; + // Release the reference to the dependent + this.injector.release(myObject); + expect(firstMapping.hasDependent(myObject)).to.be.false; + expect(secondMapping.hasDependent(myObject)).to.be.false; + }); + + it('tells the dependent object when a dependency changed', () => { + const dependentObject = { + dependencies: { + test: 'Test' + }, + onDependencyChanged: sinon.spy() + }; + const firstValue = {}; + const secondValue = {}; + this.injector.map('Test').to(firstValue); + this.injector.injectInto(dependentObject); + this.injector.override('Test').to(secondValue); + + expect(dependentObject.onDependencyChanged).to.have.been.calledWith( + 'test', secondValue + ); + }); + }); + + // ========== INJECTING DEPENDENCIES ========= + + describe('injecting dependencies', () => { + + it('injects static values', () => { + const value = {}; + this.injector.map('test').to(value); + instance = SpaceObject.create({dependencies: {value: 'test'}}); + this.injector.injectInto(instance); + expect(instance.value).to.equal(value); + }); + + it('injects into provided dependencies', () => { + const first = {dependencies: {value: 'test'}}; + const second = {dependencies: {first: 'first'}}; + this.injector.map('test').to('value'); + this.injector.map('first').to(first); + + this.injector.injectInto(second); + expect(second.first).to.equal(first); + expect(first.value).to.equal('value'); + }); + + it('handles inherited dependencies', () => { + const Base = SpaceObject.extend({dependencies: {base: 'base'}}); + const Extended = Base.extend({dependencies: {extended: 'extended'}}); + this.injector.map('base').to('base'); + this.injector.map('extended').to('extended'); + + instance = new Extended(); + this.injector.injectInto(instance); + expect(instance.base).to.equal('base'); + expect(instance.extended).to.equal('extended'); + }); + + it('never overrides existing properties', () => { + const instance = SpaceObject.create({ + dependencies: {test: 'test'}, + test: 'value' + }); + + this.injector.map('test').to('test'); + this.injector.injectInto(instance); + + expect(instance.test).to.equal('value'); + }); + + describe('when dependencies are ready', () => { + + it('tells the instance that they are ready', () => { + const value = 'test'; + const instance = SpaceObject.create({ + dependencies: {value: 'value'}, + onDependenciesReady: sinon.spy() + }); + + this.injector.map('value').to('value'); + this.injector.injectInto(instance); + this.injector.injectInto(instance); // shouldnt trigger twice; + + expect(instance.onDependenciesReady).to.have.been.calledOnce; + }); + + it('tells every single instance exactly once', () => { + const readySpy = sinon.spy(); + class TestClass extends SpaceObject { + constructor() { + super(); + this.dependencies = {value: 'test'}; + this.onDependenciesReady = readySpy; + } + } + + this.injector.map('test').to('test'); + this.injector.map('TestClass').toInstancesOf(TestClass); + + const first = this.injector.create('TestClass'); + const second = this.injector.create('TestClass'); + + expect(readySpy).to.have.been.calledTwice; + expect(readySpy).to.have.been.calledOn(first); + expect(readySpy).to.have.been.calledOn(second); + }); + }); + }); + + // ============ DEFAULT PROVIDERS ============ + + describe('default providers', () => { + + describe('static value providers', () => { + + it('maps to static value', () => { + const value = 'test'; + this.injector.map('first').to(value); + this.injector.map('second').toStaticValue(value); + + expect(this.injector.get('first')).to.equal(value); + expect(this.injector.get('second')).to.equal(value); + }); + + it('supports Space namespace lookup', () => { + Space.__test__ = {TestClass: SpaceObject.extend()}; + const path = 'Space.__test__.TestClass'; + this.injector.map(path).asStaticValue(); + + expect(this.injector.get(path)).to.equal(Space.__test__.TestClass); + delete Space.__test__; + }); + + it('can uses static toString method if available', () => { + class Test { + static toString() {return 'Test';} + } + + this.injector.map(Test).asStaticValue(); + expect(this.injector.get('Test')).to.equal(Test); + }); + }); + + describe('instance provider', () => { + + it('creates new instances for each request', () => { + class Test {} + this.injector.map('Test').toClass(Test); + + const first = this.injector.get('Test'); + const second = this.injector.get('Test'); + + expect(first).to.be.instanceof(Test); + expect(second).to.be.instanceof(Test); + expect(first).not.to.equal(second); + }); + }); + + describe('singleton provider', () => { + + it('maps class as singleton', () => { + class Test { + static toString() {return 'Test';} + } + this.injector.map(Test).asSingleton(); + const first = this.injector.get('Test'); + const second = this.injector.get('Test'); + + expect(first).to.be.instanceof(Test); + expect(first).to.equal(second); + }); + + it('maps id to singleton of class', () => { + class Test {} + this.injector.map('Test').toSingleton(Test); + const first = this.injector.get('Test'); + const second = this.injector.get('Test'); + + expect(first).to.be.instanceof(Test); + expect(first).to.equal(second); + }); + + it('looks up the value on Space namespace if only a path is given', () => { + Space.__test__ = {TestClass: SpaceObject.extend()}; + this.injector.map('Space.__test__.TestClass').asSingleton(); + + const first = this.injector.get('Space.__test__.TestClass'); + const second = this.injector.get('Space.__test__.TestClass'); + + expect(first).to.be.instanceof(Space.__test__.TestClass); + expect(first).to.equal(second); + delete Space.__test__; + }); + }); + }); + + // ============ CUSTOM PROVIDERS ============ + + describe('adding custom providers', () => { + it('adds the provider to the api', () => { + const loremIpsum = 'lorem ipsum'; + + this.injector.addProvider('toLoremIpsum', SpaceObject.extend({ + Constructor: function() { + this.provide = function() { + return loremIpsum; + }; + } + })); + this.injector.map('test').toLoremIpsum(); + expect(this.injector.get('test')).to.equal(loremIpsum); + }); + }); +}); From 2fc614b58d7062aa5a50ac02a47ff88bd39bd808 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:11:43 +0100 Subject: [PATCH 056/111] Converts helpers unit test --- tests/unit/helpers.unit.coffee | 11 ----------- tests/unit/helpers.unit.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) delete mode 100644 tests/unit/helpers.unit.coffee create mode 100644 tests/unit/helpers.unit.js diff --git a/tests/unit/helpers.unit.coffee b/tests/unit/helpers.unit.coffee deleted file mode 100644 index 9355ec2..0000000 --- a/tests/unit/helpers.unit.coffee +++ /dev/null @@ -1,11 +0,0 @@ -import Space from '../../source/space.js'; - -global = this - -describe 'Space.resolvePath', -> - - it 'returns a deeply nested object', -> - expect(Space.resolvePath 'Space.Application').to.equal Space.Application - - it 'returns the global context if path is empty', -> - expect(Space.resolvePath '').to.equal global diff --git a/tests/unit/helpers.unit.js b/tests/unit/helpers.unit.js new file mode 100644 index 0000000..90052d7 --- /dev/null +++ b/tests/unit/helpers.unit.js @@ -0,0 +1,14 @@ +import Space from '../../source/space.js'; + +const global = this; + +describe('Space.resolvePath', function() { + + it('returns a deeply nested object', () => { + expect(Space.resolvePath('Space.Application')).to.equal(Space.Application); + }); + + it('returns the global context if path is empty', () => { + expect(Space.resolvePath('')).to.equal(global); + }); +}); From 37cfbe2b6954037995c222e07b992387eca46a42 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:13:55 +0100 Subject: [PATCH 057/111] Fixes variables declarations --- tests/unit/error.tests.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/unit/error.tests.js b/tests/unit/error.tests.js index 1e2a1c6..d941c06 100644 --- a/tests/unit/error.tests.js +++ b/tests/unit/error.tests.js @@ -2,7 +2,7 @@ import SpaceError from '../../source/error.js'; describe("SpaceError", function() { - let MyError = SpaceError.extend('MyError', { + const MyError = SpaceError.extend('MyError', { message: 'The default message for this error' }); @@ -11,8 +11,8 @@ describe("SpaceError", function() { }); it("has same behavior as Space.Struct", function() { - let data = { message: 'test', code: 123 }; - let error = new MyError(data); + const data = { message: 'test', code: 123 }; + const error = new MyError(data); expect(error).to.be.instanceof(Error); expect(error).to.be.instanceof(MyError); expect(error.name).to.equal('MyError'); @@ -22,22 +22,22 @@ describe("SpaceError", function() { it("is easy to add additional fields", function() { MyError.fields = { customField: String }; - let data = { message: 'test', code: 123, customField: 'test' }; - let error = new MyError(data); + const data = { message: 'test', code: 123, customField: 'test' }; + const error = new MyError(data); expect(error.customField).to.equal('test'); MyError.fields = {}; }); it("throws the prototype message by default", function() { - let throwWithDefaultMessage = function() { + const throwWithDefaultMessage = function() { throw new MyError(); }; expect(throwWithDefaultMessage).to.throw(MyError.prototype.message); }); it("takes an optional message during construction", function() { - let myMessage = 'this is a custom message'; - let throwWithCustomMessage = function() { + const myMessage = 'this is a custom message'; + const throwWithCustomMessage = function() { throw new MyError(myMessage); }; expect(throwWithCustomMessage).to.throw(myMessage); @@ -51,13 +51,13 @@ describe("SpaceError", function() { describe("applying mixins", function() { it("supports mixin callbacks", function() { - let MyMixin = { + const MyMixin = { onConstruction: sinon.spy(), onDependenciesReady: sinon.spy() }; - let MyMixinError = SpaceError.extend('MyMixinError', { mixin: MyMixin }); - let param = 'test'; - let error = new MyMixinError(param); + const MyMixinError = SpaceError.extend('MyMixinError', { mixin: MyMixin }); + const param = 'test'; + const error = new MyMixinError(param); expect(MyMixin.onConstruction).to.have.been.calledOn(error); expect(MyMixin.onConstruction).to.have.been.calledWithExactly(param); }); From 8eee8d9a5e704082d0cbf381a6ebaa4fbac8329b Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:14:07 +0100 Subject: [PATCH 058/111] Converts Application unit test --- tests/unit/application.unit.coffee | 107 ------------------------- tests/unit/application.unit.js | 122 +++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 107 deletions(-) delete mode 100644 tests/unit/application.unit.coffee create mode 100644 tests/unit/application.unit.js diff --git a/tests/unit/application.unit.coffee b/tests/unit/application.unit.coffee deleted file mode 100644 index 96e5454..0000000 --- a/tests/unit/application.unit.coffee +++ /dev/null @@ -1,107 +0,0 @@ -import Application from '../../source/application.js'; -import Module from '../../source/module.js'; -import {Injector} from '../../source/injector.js'; - -describe 'Application', -> - - beforeEach -> - # Reset published space modules - Module.published = {} - - it 'extends Module', -> - expect(Application).to.extend Module - - describe 'construction', -> - - it 'initializes modules map as empty object', -> - expect(new Application().modules).to.eql {} - - it 'creates a new injector instance if none was given', -> - expect(new Application().injector).to.be.instanceof Injector - - it 'uses the provided injector when given', -> - injector = new Injector() - application = new Application injector: injector - expect(application.injector).to.equal injector - - it 'can also be created via static create method', -> - injector = new Injector() - application = Application.create injector: injector - expect(application.injector).to.equal injector - expect(Application.create().injector).to.be.instanceof Injector - - it 'maps injector instance with itself', -> - injector = new Injector() - injectionMapping = - to: sinon.spy() - toInstancesOf: sinon.spy() - injector.map = sinon.stub().returns injectionMapping - application = new Application injector: injector - - expect(injector.map).to.have.been.calledWithExactly 'Injector' - expect(injectionMapping.to).to.have.been.calledWithExactly injector - - it 'initializes the application', -> - initializeSpy = sinon.spy Application.prototype, 'initialize' - application = new Application() - expect(initializeSpy).to.have.been.calledOnce - initializeSpy.restore() - - it 'can be passed a configuration', -> - - @application = new Application({ - configuration: { - environment: 'testing' - } - }) - expect(@application.configuration.environment).to.equal('testing') - - it 'merges configurations of all modules and user options', -> - class GrandchildModule extends Module - @publish this, 'GrandchildModule' - configuration: { - subModuleValue: 'grandChild' - grandchild: { - toChange: 'grandchildChangeMe' - toKeep: 'grandchildKeepMe' - } - } - - class ChildModule extends Module - @publish this, 'ChildModule' - requiredModules: ['GrandchildModule'] - configuration: { - subModuleValue: 'child' - child: { - toChange: 'childChangeMe' - toKeep: 'childKeepMe' - } - } - class TestApp extends Application - requiredModules: ['ChildModule'] - configuration: { - toChange: 'appChangeMe' - subModuleValue: 'overriddenByApp' - } - app = new TestApp() - app.configure { - toChange: 'appNewValue' - child: { - toChange: 'childNewValue' - } - grandchild: { - toChange: 'grandchildNewValue' - } - } - expect(app.injector.get 'configuration').toMatch { - toChange: 'appNewValue' - subModuleValue: 'overriddenByApp' - child: { - toChange: 'childNewValue' - toKeep: 'childKeepMe' - } - grandchild: { - toChange: 'grandchildNewValue' - toKeep: 'grandchildKeepMe' - } - } diff --git a/tests/unit/application.unit.js b/tests/unit/application.unit.js new file mode 100644 index 0000000..e895c95 --- /dev/null +++ b/tests/unit/application.unit.js @@ -0,0 +1,122 @@ +import Application from '../../source/application.js'; +import Module from '../../source/module.js'; +import {Injector} from '../../source/injector.js'; + +describe('Application', function() { + + beforeEach(() => { + // Reset published space modules + Module.published = {}; + }); + + it('extends Module', () => { + expect(Application).to.extend(Module); + }); + + describe('construction', () => { + it('initializes modules map as empty object', () => { + expect(new Application().modules).to.eql({}); + }); + + it('creates a new injector instance if none was given', () => { + expect(new Application().injector).to.be.instanceof(Injector); + }); + + it('uses the provided injector when given', () => { + const injector = new Injector(); + const application = new Application({injector: injector}); + expect(application.injector).to.equal(injector); + }); + + it('can also be created via static create method', () => { + const injector = new Injector(); + const application = Application.create({injector: injector}); + expect(application.injector).to.equal(injector); + expect(Application.create().injector).to.be.instanceof(Injector); + }); + + it('maps injector instance with itself', () => { + const injector = new Injector(); + const injectionMapping = { + to: sinon.spy(), + toInstancesOf: sinon.spy() + }; + injector.map = sinon.stub().returns(injectionMapping); + const application = new Application({injector: injector}); + + expect(injector.map).to.have.been.calledWithExactly('Injector'); + expect(injectionMapping.to).to.have.been.calledWithExactly(injector); + }); + + it('initializes the application', () => { + const initializeSpy = sinon.spy(Application.prototype, 'initialize'); + const application = new Application(); + expect(initializeSpy).to.have.been.calledOnce; + initializeSpy.restore(); + }); + + it('can be passed a configuration', () => { + const application = new Application({ + configuration: { + environment: 'testing' + } + }); + expect(application.configuration.environment).to.equal('testing'); + }); + + it('merges configurations of all modules and user options', () => { + Module.define('GrandchildModule', { + configuration: { + subModuleValue: 'grandChild', + grandchild: { + toChange: 'grandchildChangeMe', + toKeep: 'grandchildKeepMe' + } + } + }); + + Module.define('ChildModule', { + requiredModules: ['GrandchildModule'], + configuration: { + subModuleValue: 'child', + child: { + toChange: 'childChangeMe', + toKeep: 'childKeepMe' + } + } + }); + + Application.extend('TestApp', { + requiredModules: ['ChildModule'], + configuration: { + toChange: 'appChangeMe', + subModuleValue: 'overriddenByApp' + } + }); + + const app = new TestApp(); + app.configure({ + toChange: 'appNewValue', + child: { + toChange: 'childNewValue' + }, + grandchild: { + toChange: 'grandchildNewValue' + } + }); + + expect(app.injector.get('configuration')).toMatch({ + toChange: 'appNewValue', + subModuleValue: 'overriddenByApp', + child: { + toChange: 'childNewValue', + toKeep: 'childKeepMe' + }, + grandchild: { + toChange: 'grandchildNewValue', + toKeep: 'grandchildKeepMe' + } + }); + }); + }); +}); From c01ceb41b239a530ce8957b47fc94f57bc02f2c8 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:14:30 +0100 Subject: [PATCH 059/111] Fixes variables declarations --- .../application_with_modules.spec.js | 6 +++--- tests/integration/lifecycle_hooks.tests.js | 20 +++++++++---------- tests/integration/module.regressions.js | 6 +++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/integration/application_with_modules.spec.js b/tests/integration/application_with_modules.spec.js index 21e040a..1e8b3bd 100644 --- a/tests/integration/application_with_modules.spec.js +++ b/tests/integration/application_with_modules.spec.js @@ -29,8 +29,8 @@ describe('Building applications based on modules', function() { it('configures module before running', function() { - let moduleValue = 'module configuration'; - let appValue = 'application configuration'; + const moduleValue = 'module configuration'; + const appValue = 'application configuration'; let testResult = null; Module.define('FirstModule', { @@ -42,7 +42,7 @@ describe('Building applications based on modules', function() { } }); - let app = Application.create({ + const app = Application.create({ requiredModules: ['FirstModule'], dependencies: { moduleValue: 'moduleValue' }, onInitialize: function() { diff --git a/tests/integration/lifecycle_hooks.tests.js b/tests/integration/lifecycle_hooks.tests.js index bd623bf..3c9305a 100644 --- a/tests/integration/lifecycle_hooks.tests.js +++ b/tests/integration/lifecycle_hooks.tests.js @@ -6,14 +6,14 @@ describe("Space.base - Application lifecycle hooks", function() { // TEST HELPERS - let addHookSpy = function(hooks, hookName) { + const addHookSpy = function(hooks, hookName) { hooks[hookName] = function() {}; sinon.spy(hooks, hookName); }; - let createLifeCycleHookSpies = function() { - hooks = {}; - hookNames = [ + const createLifeCycleHookSpies = function() { + const hooks = {}; + const hookNames = [ 'beforeInitialize', 'onInitialize', 'afterInitialize', 'beforeStart', 'onStart', 'afterStart', 'beforeReset', 'onReset', 'afterReset', 'beforeStop', 'onStop', 'afterStop' ]; @@ -23,9 +23,9 @@ describe("Space.base - Application lifecycle hooks", function() { return hooks; }; - let testOrderOfLifecycleHook = function(context, before, on, after) { - modules = ['firstHooks', 'secondHooks', 'appHooks']; - hooks = [before, on, after]; + const testOrderOfLifecycleHook = function(context, before, on, after) { + const modules = ['firstHooks', 'secondHooks', 'appHooks']; + const hooks = [before, on, after]; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { expect(context[modules[i]][hooks[j]]).to.have.been.calledOnce; @@ -38,9 +38,9 @@ describe("Space.base - Application lifecycle hooks", function() { } }; - let expectHooksNotToBeCalledYet = function(context, before, on, after) { - modules = ['firstHooks', 'secondHooks', 'appHooks']; - hooks = [before, on, after]; + const expectHooksNotToBeCalledYet = function(context, before, on, after) { + const modules = ['firstHooks', 'secondHooks', 'appHooks']; + const hooks = [before, on, after]; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { expect(context[modules[i]][hooks[j]]).not.to.have.been.called; diff --git a/tests/integration/module.regressions.js b/tests/integration/module.regressions.js index 72358ea..d2ebd5f 100644 --- a/tests/integration/module.regressions.js +++ b/tests/integration/module.regressions.js @@ -10,8 +10,8 @@ describe("Module - regressions", function() { Test = Space.namespace('Test'); SomeLib = { libMethod: function() {} }; - let singletonReadySpy = sinon.spy(); - let myInjector = new Injector(); + const singletonReadySpy = sinon.spy(); + const myInjector = new Injector(); SpaceObject.extend(Test, 'MySingleton', { dependencies: { someLib: 'SomeLib' }, @@ -23,7 +23,7 @@ describe("Module - regressions", function() { onInitialize() { this.injector.map('SomeLib').to(SomeLib); } }); - let module = new Test.MyModule(); + const module = new Test.MyModule(); module.initialize(module, myInjector); expect(singletonReadySpy).to.have.been.called; From d44dfa36dde26b26c5e4877bbdb569c409711b0c Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:17:12 +0100 Subject: [PATCH 060/111] Updates package.json with: keywords, contributors, Pins npm to >= 1.4.* --- package.json | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 627554b..7eaed42 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,29 @@ "name": "space-base", "version": "4.1.4", "description": "Modular Application Architecture", + "keywords": [ + "space", + "modular", + "architecture", + "dependencies", + "dependency", + "injection", + "di", + "ioc", + "inversion of control" + ], + "author": "Dominik Guzei, Rhys Bartels-Waller, Darko Mijic, Adam Desivi", + "contributors": [ + "Dominik Guzei (https://github.com/DominikGuzei)", + "Rhys Bartels-Waller (https://github.com/rhyslbw)", + "Darko Mijić (https://github.com/darko-mijic)", + "Jonas Aschenbrenner (https://github.com/Sanjo)", + "Adam Desivi (https://github.com/qejk)" + ], + "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">= 4.0.x", + "npm": ">= 1.4.x" }, "dependencies": { "underscore": "1.8.*", @@ -11,7 +32,5 @@ "lodash": "4.17.*" }, "devDependencies": {}, - "scripts": {}, - "author": "Dominik Guzei, Rhys Bartels-Waller, Darko Mijic, Adam Desivi", - "license": "MIT" + "scripts": {} } \ No newline at end of file From 4490b2d3491af79699bd99fffdd2c94a9c77d1ff Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:17:53 +0100 Subject: [PATCH 061/111] Updates meteor package.js with new file paths --- package.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.js b/package.js index 028da22..e9d4ebf 100644 --- a/package.js +++ b/package.js @@ -71,13 +71,13 @@ Package.onTest(function(api) { api.addFiles([ // unit tests - 'tests/unit/object.unit.coffee', - 'tests/unit/module.unit.coffee', - 'tests/unit/struct.unit.coffee', - 'tests/unit/application.unit.coffee', - 'tests/unit/injector.unit.coffee', + 'tests/unit/object.unit.js', + 'tests/unit/module.unit.js', + 'tests/unit/struct.unit.js', + 'tests/unit/application.unit.js', + 'tests/unit/injector.unit.js', 'tests/unit/injector_annotations.unit.js', - 'tests/unit/helpers.unit.coffee', + 'tests/unit/helpers.unit.js', 'tests/unit/error.tests.js', 'tests/unit/logger.tests.js', From 05d1adc4da22b5373cc5ce55902897555fd50d09 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:18:09 +0100 Subject: [PATCH 062/111] Adds a fallback to SpaceObject for ES6 class definitions --- source/object.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/object.coffee b/source/object.coffee index 8e56c27..7469a7d 100644 --- a/source/object.coffee +++ b/source/object.coffee @@ -8,6 +8,9 @@ class SpaceObject # Assign given properties to the instance constructor: (properties) -> + unless @constructor.classPath? + @constructor.type(@constructor.name); + @_invokeConstructionCallbacks.apply(this, arguments) # Copy properties to instance by default @[key] = value for key, value of properties From 227fcebbf0fe8d6b5de168dd70562f006140625e Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:27:06 +0100 Subject: [PATCH 063/111] Fixes variables declarations --- tests/unit/object.unit.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/object.unit.js b/tests/unit/object.unit.js index 504b635..8be7486 100644 --- a/tests/unit/object.unit.js +++ b/tests/unit/object.unit.js @@ -64,7 +64,7 @@ describe('SpaceObject', function() { it("assigns the class path", () => { const className = 'My.custom.Class'; - MyClass = SpaceObject.extend(className); + const MyClass = SpaceObject.extend(className); expect(MyClass.toString()).to.equal(className); expect(new MyClass().toString()).to.equal(className); }); @@ -72,7 +72,7 @@ describe('SpaceObject', function() { it("exposes the class on the global scope if possible", () => { const my = {}; my.namespace = Space.namespace('my.namespace'); - MyClass = SpaceObject.extend('my.namespace.MyClass'); + const MyClass = SpaceObject.extend('my.namespace.MyClass'); expect(my.namespace.MyClass).to.equal(MyClass); }); @@ -86,13 +86,13 @@ describe('SpaceObject', function() { it('allows you to define static class properties', () => { const myStatics = {}; - MyClass = SpaceObject.extend({statics: {myStatics: myStatics}}); + const MyClass = SpaceObject.extend({statics: {myStatics: myStatics}}); expect(MyClass.myStatics).to.equal(myStatics); }); it('provides an api for defining a callback while extending', () => { const onExtendingSpy = sinon.spy(); - MyClass = SpaceObject.extend({onExtending: onExtendingSpy}); + const MyClass = SpaceObject.extend({onExtending: onExtendingSpy}); expect(onExtendingSpy).to.have.been.calledOn(MyClass); }); }); From 05d2e8086dd4f84db2a2f2d4b41156221bd25b28 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:42:15 +0100 Subject: [PATCH 064/111] Updates testing driver to Mocha --- ci.sh | 9 +++++++++ circle.yml | 3 ++- package.js | 5 ++++- test.sh | 25 +++++++++++++++++++++---- 4 files changed, 36 insertions(+), 6 deletions(-) create mode 100755 ci.sh diff --git a/ci.sh b/ci.sh new file mode 100755 index 0000000..b9b7758 --- /dev/null +++ b/ci.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +export PACKAGE_DIRS='packages' + +if [ "$PORT" ]; then + spacejam test-packages ./ --driver-package=practicalmeteor:mocha-console-runner --port $PORT +else + spacejam test-packages ./ --driver-package=practicalmeteor:mocha-console-runner +fi \ No newline at end of file diff --git a/circle.yml b/circle.yml index 2ab0262..f2dd49c 100644 --- a/circle.yml +++ b/circle.yml @@ -11,4 +11,5 @@ dependencies: test: override: - - spacejam test-packages ./ + - ./ci.sh + diff --git a/package.js b/package.js index e9d4ebf..93bd414 100644 --- a/package.js +++ b/package.js @@ -64,7 +64,10 @@ Package.onTest(function(api) { 'space:base', // weak-dependencies - 'practicalmeteor:munit@2.1.5', + 'practicalmeteor:mocha@2.4.5_6', + 'practicalmeteor:chai@2.1.0_1', + 'practicalmeteor:sinon@1.14.1_2', + 'space:testing@3.0.1' ]); diff --git a/test.sh b/test.sh index 9bcd4a4..3ee5a6e 100755 --- a/test.sh +++ b/test.sh @@ -1,7 +1,24 @@ -#!/usr/bin/env bash +# !/usr/bin/env bash -if [ "$PORT" ]; then - meteor test-packages ./ --port $PORT +if [ "$TEST_DRIVER" ]; +then + TEST_DRIVER=$TEST_DRIVER else - meteor test-packages ./ + TEST_DRIVER="practicalmeteor:mocha" fi + +if [ "$METEOR_PACKAGE_DIRS" ]; +then + PACKAGE_DIRS=$METEOR_PACKAGE_DIRS +else + if [ "$PACKAGE_DIRS" ]; + then + PACKAGE_DIRS=$PACKAGE_DIRS + else + PACKAGE_DIRS="packages" + fi +fi + +export METEOR_PACKAGE_DIRS=$PACKAGE_DIRS + +eval "meteor test-packages ./ --driver-package $TEST_DRIVER $*" \ No newline at end of file From 1e928db5fc710f8f9a1557c2659d125f8e7f4869 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:42:38 +0100 Subject: [PATCH 065/111] Updates tests to run with Mocha --- tests/integration/lifecycle_hooks.tests.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/lifecycle_hooks.tests.js b/tests/integration/lifecycle_hooks.tests.js index 3c9305a..7e24683 100644 --- a/tests/integration/lifecycle_hooks.tests.js +++ b/tests/integration/lifecycle_hooks.tests.js @@ -48,7 +48,7 @@ describe("Space.base - Application lifecycle hooks", function() { } }; - beforeEach(function() { + beforeEach(() => { // reset published space modules Module.published = {}; // Setup lifecycle hooks with sinon spys @@ -61,24 +61,24 @@ describe("Space.base - Application lifecycle hooks", function() { this.app = Application.create(_.extend(this.appHooks, { requiredModules: ['Second'] })); }); - it("runs the initialize hooks in correct order", function() { + it("runs the initialize hooks in correct order", () => { testOrderOfLifecycleHook(this, 'beforeInitialize', 'onInitialize', 'afterInitialize'); }); - it("runs the start hooks in correct order", function() { + it("runs the start hooks in correct order", () => { expectHooksNotToBeCalledYet(this, 'beforeStart', 'onStart', 'afterStart'); this.app.start(); testOrderOfLifecycleHook(this, 'beforeStart', 'onStart', 'afterStart'); }); - it("runs the stop hooks in correct order", function() { + it("runs the stop hooks in correct order", () => { expectHooksNotToBeCalledYet(this, 'beforeStop', 'onStop', 'afterStop'); this.app.start(); this.app.stop(); testOrderOfLifecycleHook(this, 'beforeStop', 'onStop', 'afterStop'); }); - it("runs the reset hooks in correct order when app is running", function() { + it("runs the reset hooks in correct order when app is running", () => { expectHooksNotToBeCalledYet(this, 'beforeReset', 'onReset', 'afterReset'); this.app.start(); this.app.reset(); @@ -87,7 +87,7 @@ describe("Space.base - Application lifecycle hooks", function() { 'onStart', 'afterStart'); }); - it("runs the reset hooks in correct order when app is stopped", function() { + it("runs the reset hooks in correct order when app is stopped", () => { expectHooksNotToBeCalledYet(this, 'beforeReset', 'onReset', 'afterReset'); this.app.reset(); testOrderOfLifecycleHook(this, 'beforeReset', 'onReset', 'afterReset'); From d8e362a4f1ad3d489fcb33da5940c92cd9c6143b Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 10:42:54 +0100 Subject: [PATCH 066/111] Skips temporarily Meteor oriented tests --- tests/unit/module.unit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/module.unit.js b/tests/unit/module.unit.js index aaddfb5..b2eaec7 100644 --- a/tests/unit/module.unit.js +++ b/tests/unit/module.unit.js @@ -91,7 +91,7 @@ describe('Module - #initialize', () => { expect(this.module.is('initialized')).to.be.true; }); - it.server('server adds Npm as property to the module', () => { + xit('server adds Npm as property to the module', () => { this.module.initialize(this.app, this.injector); expect(this.module.npm.require).to.be.defined; }); @@ -171,7 +171,7 @@ describe('Module - #reset', () => { this.module._runLifeCycleAction = sinon.spy(); }); - it.server('rejects attempts to reset when in production', () => { + xit('rejects attempts to reset when in production', () => { const nodeEnvBackup = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; this.module.reset(); From 910a51a78196edf596d993d2dd0baedf4fdf4e47 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 11:39:04 +0100 Subject: [PATCH 067/111] Fixes CircleCI configuration for Mocha for failing tests with updated spacejam@rc --- ci.sh | 9 --------- circle.yml | 5 ++--- 2 files changed, 2 insertions(+), 12 deletions(-) delete mode 100755 ci.sh diff --git a/ci.sh b/ci.sh deleted file mode 100755 index b9b7758..0000000 --- a/ci.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -export PACKAGE_DIRS='packages' - -if [ "$PORT" ]; then - spacejam test-packages ./ --driver-package=practicalmeteor:mocha-console-runner --port $PORT -else - spacejam test-packages ./ --driver-package=practicalmeteor:mocha-console-runner -fi \ No newline at end of file diff --git a/circle.yml b/circle.yml index f2dd49c..f20d2cd 100644 --- a/circle.yml +++ b/circle.yml @@ -7,9 +7,8 @@ dependencies: cache_directories: - node_modules pre: - - npm install -g spacejam + - npm install -g spacejam@rc test: override: - - ./ci.sh - + - spacejam test-packages --driver-package=practicalmeteor:mocha-console-runner ./ From e4412a514fb9dbf0a988651ef70434ffab312513 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 14:26:34 +0100 Subject: [PATCH 068/111] Converts SpaceObject --- source/object.coffee | 314 ---------------------------------- source/object.js | 392 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 392 insertions(+), 314 deletions(-) delete mode 100644 source/object.coffee create mode 100644 source/object.js diff --git a/source/object.coffee b/source/object.coffee deleted file mode 100644 index 7469a7d..0000000 --- a/source/object.coffee +++ /dev/null @@ -1,314 +0,0 @@ -import _ from 'underscore'; -import {ensure, oneOf, anything} from 'simplecheck'; -import Space from './space.js'; - -class SpaceObject - - # ============= PUBLIC PROTOTYPE ============== # - - # Assign given properties to the instance - constructor: (properties) -> - unless @constructor.classPath? - @constructor.type(@constructor.name); - - @_invokeConstructionCallbacks.apply(this, arguments) - # Copy properties to instance by default - @[key] = value for key, value of properties - - onDependenciesReady: -> - # Let mixins initialize themselves when dependencies are ready - for mixin in @constructor._getAppliedMixins() - mixin.onDependenciesReady?.call(this) - - toString: -> @constructor.toString() - - hasSuperClass: -> @constructor.__super__? - - # Returns either the super class constructor (if no param given) or - # the prototype property or method with [key] - superClass: (key) -> - sup = @constructor.__super__.constructor - if key? then sup.prototype[key] else sup - - # Returns true if the passed in mixin has been applied to this or a super class - hasMixin: (mixin) -> _.contains(@constructor._getAppliedMixins(), mixin) - - # This method needs to stay separate from the constructor so that - # SpaceError can use it too! - _invokeConstructionCallbacks: -> - # Let mixins initialize themselves on construction - for mixin in @constructor._getAppliedMixins() - mixin.onConstruction?.apply(this, arguments) - - # ============= PUBLIC STATIC ============== # - - # Extends this class and return a child class with inherited prototype - # and static properties. - # - # There are various ways you can call this method: - # - # 1. SpaceObject.extend() - # -------------------------------------------- - # Creates an anonymous child class without extra prototype properties. - # Basically the same as `class extend SpaceObject` in coffeescript - # - # 2. SpaceObject.extend(className) - # -------------------------------------------- - # Creates a named child class without extra prototype properties. - # Basically the same as `class ClassName extend SpaceObject` in coffeescript - # - # 3. SpaceObject.extend(classPath) - # -------------------------------------------- - # Creates a child class with fully qualified class path like "my.custom.Class" - # assigned and registered internally so that Space.resolvePath can find it. - # This also assigns the class path as type, which can be used for serialization - # - # 4. SpaceObject.extend({ prop: 'first', … }) - # -------------------------------------------- - # Creates an anonymous child class with extra prototype properties. - # Same as: - # class extend SpaceObject - # prop: 'first' - # - # 5. SpaceObject.extend(namespace, className) - # -------------------------------------------- - # Creates a named class which inherits from SpaceObject and assigns - # it to the given namespace object. - # - # 6. SpaceObject.extend(className, prototype) - # -------------------------------------------- - # Creates a named class which inherits from SpaceObject and extra prototype - # properties which are assigned to the new class - # - # 7. SpaceObject.extend(classPath, prototype) - # -------------------------------------------- - # Creates a registered class which inherits from SpaceObject and extra prototype - # properties which are assigned to the new class - # - # 8. SpaceObject.extend(namespace, className, prototype) - # -------------------------------------------- - # Creates a named class which inherits from SpaceObject, has extra prototype - # properties and is assigned to the given namespace. - @extend: (args...) -> - - # Defaults - namespace = {} - classPath = null - className = '_Class' # Same as coffeescript - extension = {} - - # Only one param: (extension) OR (className) OR (classPath) -> - if args.length is 1 - if _.isObject(args[0]) then extension = args[0] - if _.isString(args[0]) - # (className) OR (classPath) - if args[0].indexOf('.') != -1 - # classPath - classPath = args[0] - className = classPath.substr(classPath.lastIndexOf('.') + 1) - else - # className - className = classPath = args[0] - - # Two params must be: (namespace, className) OR (className, extension) -> - if args.length is 2 - if _.isObject(args[0]) and _.isString(args[1]) - namespace = args[0] - className = args[1] - extension = {} - else if _.isString(args[0]) and _.isObject(args[1]) - # (className) OR (classPath) - namespace = {} - extension = args[1] - if args[0].indexOf('.') != -1 - # classPath - classPath = args[0] - className = classPath.substr(classPath.lastIndexOf('.') + 1) - else - # className - className = classPath = args[0] - - # All three params: (namespace, className, extension) -> - if args.length is 3 - namespace = args[0] - className = args[1] - extension = args[2] - - ensure namespace, oneOf(anything, Space.Namespace, Function) - ensure classPath, oneOf(String, null) - ensure className, String - ensure extension, anything - - # Assign the optional custom constructor for this class - Parent = this - Constructor = extension.Constructor ? -> Parent.apply(this, arguments) - - # Create a named constructor for this class so that debugging - # consoles are displaying the class name nicely. - Child = new Function('initialize', 'return function ' + className + '() { - initialize.apply(this, arguments); - }')(Constructor) - - # Add subclass to parent class - Parent._subClasses.push(Child) - - # Copy the static properties of this class over to the extended - Child[key] = this[key] for key of this - Child._subClasses = [] - - # Copy over static class properties defined on the extension - if extension.statics? - _.extend Child, extension.statics - delete extension.statics - - # Extract mixins before they get added to prototype - mixins = extension.mixin - delete extension.mixin - - # Extract onExtending callback and avoid adding it to prototype - onExtendingCallback = extension.onExtending - delete extension.onExtending - - # Javascript prototypal inheritance "magic" - Ctor = -> - @constructor = Child - return - Ctor.prototype = Parent.prototype - Child.prototype = new Ctor() - Child.__super__ = Parent.prototype - - # Apply mixins - if mixins? then Child.mixin(mixins) - - # Merge the extension into the class prototype - @_mergeIntoPrototype Child.prototype, extension - - # Add the class to the namespace - if namespace? - namespace[className] = Child - if namespace instanceof Space.Namespace - classPath = "#{namespace.getPath()}.#{className}" - - # Add type information to the class - Child.type classPath if classPath? - - # Invoke the onExtending callback after everything has been setup - onExtendingCallback?.call(Child) - - return Child - - @type: (@classPath) -> - # Register this class with its class path - Space.registry[@classPath] = this - try - # Add the class to the resolved namespace - path = @classPath.substr 0, @classPath.lastIndexOf('.') - namespace = Space.resolvePath path - className = @classPath.substr(@classPath.lastIndexOf('.') + 1) - namespace[className] = this - - # Create and instance of the class that this method is called on - # e.g.: SpaceObject.create() would return an instance of SpaceObject - @create: -> - # Use a wrapper class to hand the constructor arguments - # to the context class that #create was called on - args = arguments - Context = this - wrapper = -> Context.apply this, args - wrapper extends Context - new wrapper() - - # Mixin properties and methods to the class prototype and merge - # properties that are plain objects to support the mixin of configs etc. - @mixin: (mixins) -> - if _.isArray(mixins) - @_applyMixin(mixin) for mixin in mixins - else - @_applyMixin(mixins) - - # Returns true if this class has a super class - @hasSuperClass: -> @__super__? - - @isSubclassOf: (sup) -> - isSubclass = this.prototype instanceof sup - isSameClass = this is sup - return isSubclass || isSameClass - - # Returns either the super class constructor (if no param given) or - # the static property or method with [key] - @superClass: (key) -> - return undefined if !@__super__? - sup = @__super__.constructor - if key? then sup[key] else sup - - # Returns a flat, uniq array of all sub classes - @subClasses: -> - subs = [].concat(@_subClasses) - subs = subs.concat(subClass.subClasses()) for subClass in subs - return _.uniq(subs) - - # Returns true if the passed in mixin has been applied to this or a super class - @hasMixin: (mixin) -> _.contains(@_getAppliedMixins(), mixin) - - # ============= PRIVATE STATIC ============== # - - @_subClasses: [] - - @_applyMixin: (mixin) -> - # Add the original mixin to the registry so we can ask if a specific - # mixin has been added to a host class / instance - # Each class has its own mixins array - hasMixins = @_appliedMixins? - areInherited = hasMixins and @superClass('_appliedMixins') is @_appliedMixins - if !hasMixins or areInherited then @_appliedMixins = [] - - # Keep the mixins array clean from duplicates - @_appliedMixins.push(mixin) if !_.contains(@_appliedMixins, mixin) - - # Create a clone so that we can remove properties without affecting the global mixin - mixinCopy = _.clone mixin - - # Remove hooks from mixin, so that they are not added to host class - delete mixinCopy.onDependenciesReady - delete mixinCopy.onConstruction - - # Mixin static properties into the host class - if mixinCopy.statics? - statics = mixinCopy.statics - _.extend(this, statics) - _.extend(sub, statics) for sub in @subClasses() - delete mixinCopy.statics - - # Give mixins the chance to do static setup when applied to the host class - mixinCopy.onMixinApplied?.call this - delete mixinCopy.onMixinApplied - - # Copy over the mixin to the prototype and merge objects - @_mergeIntoPrototype @prototype, mixinCopy - - @_getAppliedMixins: -> - mixins = [] - mixins = mixins.concat(@superClass()._getAppliedMixins()) if @hasSuperClass() - mixins = mixins.concat(@_appliedMixins) if @_appliedMixins? - return _.uniq(mixins) - - @_mergeIntoPrototype: (prototype, extension) -> - # Helper function to check for object literals only - isPlainObject = (value) -> - _.isObject(value) and !_.isArray(value) and !_.isFunction(value) - for key, value of extension - hasProperty = prototype.hasOwnProperty(key) - if hasProperty and isPlainObject(value) and isPlainObject(prototype[key]) - # Deep extend plain objects - _.deepExtend(prototype[key], _.clone(value)) - else - value = _.clone(value) if isPlainObject(value) - # Set non-existing props and override existing methods - prototype[key] = value - -# Incompatible with ES6 import/export without fallback -# Fallback to original result from toString if classPath is not defined -toStringFallback = SpaceObject.toString.bind(SpaceObject) -SpaceObject.toString = -> @classPath || toStringFallback() - -export default SpaceObject; diff --git a/source/object.js b/source/object.js new file mode 100644 index 0000000..540de15 --- /dev/null +++ b/source/object.js @@ -0,0 +1,392 @@ +import _ from 'underscore'; +import {isNil, entries as ObjectEntries, isPlainObject} from 'lodash'; +import {ensure, oneOf, anything} from 'simplecheck'; +import Space from './space.js'; + +class SpaceObject { + // ============= PUBLIC PROTOTYPE ============== # + + // Assign given properties to the instance + constructor(properties) { + // ES6 fallback + if (isNil(this.constructor.classPath)) { + this.constructor.type(this.constructor.name); + } + + this._invokeConstructionCallbacks.apply(this, arguments); + // Copy properties to instance by default + for (let [key, value] of ObjectEntries(properties)) { + this[key] = value; + } + } + + onDependenciesReady() { + // Let mixins initialize themselves when dependencies are ready + for (let mixin of this.constructor._getAppliedMixins()) { + if (mixin.onDependenciesReady) {mixin.onDependenciesReady.call(this);} + } + } + + toString() { + return this.constructor.toString(); + } + + hasSuperClass() { + return (!isNil(this.constructor.__super__)); + } + + // Returns either the super class constructor (if no param given) or + // the prototype property or method with [key] + superClass(key) { + let sup = this.constructor.__super__.constructor; + if (!isNil(key)) { + return sup.prototype[key]; + } else { + return sup; + } + } + + // Returns true if the passed in mixin has been applied to this or a super class + hasMixin(mixin) { + return _.contains(this.constructor._getAppliedMixins(), mixin); + } + + // This method needs to stay separate from the constructor so that + // SpaceError can use it too! + _invokeConstructionCallbacks() { + if (isNil(this.constructor._getAppliedMixins)) { + return; + } + // Let mixins initialize themselves on construction + for (let mixin of this.constructor._getAppliedMixins()) { + if (mixin.onConstruction) {mixin.onConstruction.apply(this, arguments);} + } + } + + // ============= PUBLIC STATIC ============== # + + // Extends this class and return a child class with inherited prototype + // and static properties. + // + // There are various ways you can call this method: + // + // 1. SpaceObject.extend() + // -------------------------------------------- + // Creates an anonymous child class without extra prototype properties. + // Basically the same as `class extend SpaceObject` in coffeescript + // + // 2. SpaceObject.extend(className) + // -------------------------------------------- + // Creates a named child class without extra prototype properties. + // Basically the same as `class ClassName extend SpaceObject` in coffeescript + // + // 3. SpaceObject.extend(classPath) + // -------------------------------------------- + // Creates a child class with fully qualified class path like "my.custom.Class" + // assigned and registered internally so that Space.resolvePath can find it. + // This also assigns the class path as type, which can be used for serialization + // + // 4. SpaceObject.extend({ prop: 'first', … }) + // -------------------------------------------- + // Creates an anonymous child class with extra prototype properties. + // Same as: + // class extend SpaceObject + // prop: 'first' + // + // 5. SpaceObject.extend(namespace, className) + // -------------------------------------------- + // Creates a named class which inherits from SpaceObject and assigns + // it to the given namespace object. + // + // 6. SpaceObject.extend(className, prototype) + // -------------------------------------------- + // Creates a named class which inherits from SpaceObject and extra prototype + // properties which are assigned to the new class + // + // 7. SpaceObject.extend(classPath, prototype) + // -------------------------------------------- + // Creates a registered class which inherits from SpaceObject and extra prototype + // properties which are assigned to the new class + // + // 8. SpaceObject.extend(namespace, className, prototype) + // -------------------------------------------- + // Creates a named class which inherits from SpaceObject, has extra prototype + // properties and is assigned to the given namespace. + static extend(...args) { + // Defaults + let namespace = {}; + let classPath = null; + let className = '_Class'; // Same as coffeescript + let extension = {}; + + // Only one param: (extension) OR (className) OR (classPath) -> + if (args.length === 1) { + if (_.isObject(args[0])) { extension = args[0]; } + if (_.isString(args[0])) { + // (className) OR (classPath) + if (args[0].indexOf('.') !== -1) { + // classPath + classPath = args[0]; + className = classPath.substr(classPath.lastIndexOf('.') + 1); + } else { + // className + className = classPath = args[0]; + } + } + } + + // Two params must be: (namespace, className) OR (className, extension) -> + if (args.length === 2) { + if (_.isObject(args[0]) && _.isString(args[1])) { + namespace = args[0]; + className = args[1]; + extension = {}; + } else if (_.isString(args[0]) && _.isObject(args[1])) { + // (className) OR (classPath) + namespace = {}; + extension = args[1]; + if (args[0].indexOf('.') !== -1) { + // classPath + classPath = args[0]; + className = classPath.substr(classPath.lastIndexOf('.') + 1); + } else { + // className + className = classPath = args[0]; + } + } + } + + // All three params: (namespace, className, extension) -> + if (args.length === 3) { + namespace = args[0]; + className = args[1]; + extension = args[2]; + } + + ensure(namespace, oneOf(anything, Space.Namespace, Function)); + ensure(classPath, oneOf(String, null)); + ensure(className, String); + ensure(extension, anything); + + // Assign the optional custom constructor for this class + let Parent = this; + let Constructor = !isNil(extension.Constructor) ? + extension.Constructor : + function() { return Parent.apply(this, arguments); }; + + // Create a named constructor for this class so that debugging + // consoles are displaying the class name nicely. + let Child = new Function('initialize', `return function ${className}` + `() { \ + initialize.apply(this, arguments); \ + }`)(Constructor); + + // Add subclass to parent class + Parent._subClasses.push(Child); + + // Copy the static properties of this class over to the extended + for (let key in this) { Child[key] = this[key]; } + Child._subClasses = []; + + // Copy over static class properties defined on the extension + if (!isNil(extension.statics)) { + _.extend(Child, extension.statics); + delete extension.statics; + } + + // Extract mixins before they get added to prototype + let mixins = extension.mixin; + delete extension.mixin; + + // Extract onExtending callback and avoid adding it to prototype + let onExtendingCallback = extension.onExtending; + delete extension.onExtending; + + // Javascript prototypal inheritance "magic" + let Ctor = function() { + this.constructor = Child; + }; + Ctor.prototype = Parent.prototype; + Child.prototype = new Ctor(); + Child.__super__ = Parent.prototype; + + // Apply mixins + if (!isNil(mixins)) {Child.mixin(mixins);} + + // Merge the extension into the class prototype + this._mergeIntoPrototype(Child.prototype, extension); + + // Add the class to the namespace + if (!isNil(namespace)) { + namespace[className] = Child; + if (namespace instanceof Space.Namespace) { + classPath = `${namespace.getPath()}.${className}`; + } + } + + // Add type information to the class + if (!isNil(classPath)) {Child.type(classPath);} + + // Invoke the onExtending callback after everything has been setup + if (!isNil(onExtendingCallback)) { + onExtendingCallback.call(Child); + } + + return Child; + } + + static type(classPath) { + // Register this class with its class path + this.classPath = classPath; + Space.registry[this.classPath] = this; + try { + // Add the class to the resolved namespace + let path = this.classPath.substr(0, this.classPath.lastIndexOf('.')); + let namespace = Space.resolvePath(path); + let className = this.classPath.substr(this.classPath.lastIndexOf('.') + 1); + return namespace[className] = this; + } catch (error) {} + } + + // Create and instance of the class that this method is called on + // e.g.: SpaceObject.create() would return an instance of SpaceObject + static create() { + // Use a wrapper class to hand the constructor arguments + // to the context class that #create was called on + let args = arguments; + let Context = this; + let wrapper = function() { return Context.apply(this, args); }; + __extends__(wrapper, Context); + return new wrapper(); + } + + // Mixin properties and methods to the class prototype and merge + // properties that are plain objects to support the mixin of configs etc. + static mixin(mixins) { + if (_.isArray(mixins)) { + return Array.from(mixins).map((mixin) => this._applyMixin(mixin)); + } else { + return this._applyMixin(mixins); + } + } + + // Returns true if this class has a super class + static hasSuperClass() { return (!isNil(this.__super__)); } + + static isSubclassOf(sup) { + let isSubclass = this.prototype instanceof sup; + let isSameClass = this === sup; + return isSubclass || isSameClass; + } + + // Returns either the super class constructor (if no param given) or + // the static property or method with [key] + static superClass(key) { + if (isNil(this.__super__)) { return undefined; } + let sup = this.__super__.constructor; + if (!isNil(key)) { return sup[key]; } else { return sup; } + } + + // Returns a flat, uniq array of all sub classes + static subClasses() { + let subs = [].concat(this._subClasses); + for (let subClass of subs) { + subs = subs.concat(subClass.subClasses()); + } + return _.uniq(subs); + } + + // Returns true if the passed in mixin has been applied to this or a super class + static hasMixin(mixin) { + return _.contains(this._getAppliedMixins(), mixin); + } + + static _applyMixin(mixin) { + // Add the original mixin to the registry so we can ask if a specific + // mixin has been added to a host class / instance + // Each class has its own mixins array + hasMixins = !isNil(this._appliedMixins); + areInherited = ( + hasMixins && this.superClass('_appliedMixins') === this._appliedMixins + ); + if (!hasMixins || areInherited) { + this._appliedMixins = []; + } + + // Keep the mixins array clean from duplicates + if (!_.contains(this._appliedMixins, mixin)) { + this._appliedMixins.push(mixin); + } + + // Create a clone so that we can remove properties without affecting the global + // mixin + mixinCopy = _.clone(mixin); + + // Remove hooks from mixin, so that they are not added to host class + delete mixinCopy.onDependenciesReady; + delete mixinCopy.onConstruction; + + // Mixin static properties into the host class + if (!isNil(mixinCopy.statics)) { + const statics = mixinCopy.statics; + _.extend(this, statics); + for (let sub of this.subClasses()) { + _.extend(sub, statics); + } + delete mixinCopy.statics; + } + + // Give mixins the chance to do static setup when applied to the host class + if (!isNil(mixinCopy.onMixinApplied)) { + mixinCopy.onMixinApplied.call(this); + } + delete mixinCopy.onMixinApplied; + + // Copy over the mixin to the prototype and merge objects + this._mergeIntoPrototype(this.prototype, mixinCopy); + } + + static _getAppliedMixins() { + let mixins = []; + if (this.hasSuperClass() && !isNil(this.superClass()._getAppliedMixins)) { + mixins = mixins.concat(this.superClass()._getAppliedMixins()); + } + if (!isNil(this._appliedMixins)) { + mixins = mixins.concat(this._appliedMixins); + } + return _.uniq(mixins); + } + + static _mergeIntoPrototype(prototype, extension) { + for (let [key, value] of ObjectEntries(extension)) { + hasProperty = prototype.hasOwnProperty(key); + if (hasProperty && isPlainObject(value) && isPlainObject(prototype[key])) { + // Deep extend plain objects + _.deepExtend(prototype[key], _.clone(value)); + } else { + if (isPlainObject(value)) { + value = _.clone(value); + } + // Set non-existing props and override existing methods + prototype[key] = value; + } + } + } +} + +SpaceObject._subClasses = []; + +// Incompatible with ES6 import/export without fallback +// Fallback to original result from toString if classPath is not defined +let toStringFallback = SpaceObject.toString.bind(SpaceObject); +SpaceObject.toString = function() { return this.classPath || toStringFallback(); }; + + +function __extends__(child, parent) { + Object.getOwnPropertyNames(parent).forEach( + name => child[name] = parent[name] + ); + child.prototype = Object.create(parent.prototype); + child.__super__ = parent.prototype; + return child; +} +export default SpaceObject; From 86ea282dd4ecf89f0e0e76f2c5be16a44181ea3c Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 14:27:14 +0100 Subject: [PATCH 069/111] Converts Struct --- source/struct.coffee | 23 ----------------------- source/struct.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 23 deletions(-) delete mode 100644 source/struct.coffee create mode 100644 source/struct.js diff --git a/source/struct.coffee b/source/struct.coffee deleted file mode 100644 index 09ba1ed..0000000 --- a/source/struct.coffee +++ /dev/null @@ -1,23 +0,0 @@ -import _ from 'underscore'; -import {ensure} from 'simplecheck'; -import SpaceObject from './object.coffee'; - -class Struct extends SpaceObject - - @fields: {} - - constructor: (data={}) -> - @_checkFields(data) - super - - fields: -> _.clone(@constructor.fields) ? {} - - toPlainObject: -> - copy = {} - copy[key] = @[key] for key of @fields() when @[key] != undefined - return copy - - # Use the fields configuration to check given data during runtime - _checkFields: (data) -> ensure data, @fields() - -export default Struct; diff --git a/source/struct.js b/source/struct.js new file mode 100644 index 0000000..7df52ce --- /dev/null +++ b/source/struct.js @@ -0,0 +1,37 @@ +import _ from 'underscore'; +import {ensure} from 'simplecheck'; +import {isNil} from 'lodash'; +import SpaceObject from './object.js'; + +class Struct extends SpaceObject { + + constructor(data = {}) { + super(data); + this._checkFields(data); + } + + fields() { + return _.clone(this.constructor.fields) || {}; + } + + toPlainObject() { + const copy = {}; + for (let key of Object.keys(this.fields())) { + if (!isNil(this[key])) { + copy[key] = this[key]; + } + } + return copy; + } + + // Use the fields configuration to check given data during runtime + _checkFields(data) { + ensure(data, this.fields()); + } + + statics: { + fields: {} + } +} + +export default Struct; From ae476d7120e6ae91c7bd83fac254fbac13ca5210 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 14:30:36 +0100 Subject: [PATCH 070/111] =?UTF-8?q?Updates=20imports=20to=20use=20converte?= =?UTF-8?q?d=20SpaceObject,=20replaces=20tests=20with=20chai=20custom=20as?= =?UTF-8?q?sertion=20=E2=80=98extend=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.js | 5 +++-- source/error.js | 6 +++--- source/index.js | 4 ++-- source/logger.js | 2 +- source/loggers/adapter.js | 2 +- source/module.js | 2 +- tests/integration/module.regressions.js | 2 +- tests/unit/application.unit.js | 2 +- tests/unit/injector.unit.js | 2 +- tests/unit/logger.tests.js | 4 ++-- tests/unit/module.unit.js | 4 ++-- tests/unit/object.unit.js | 2 +- tests/unit/struct.unit.js | 8 ++++---- 13 files changed, 23 insertions(+), 22 deletions(-) diff --git a/package.js b/package.js index 93bd414..951a664 100644 --- a/package.js +++ b/package.js @@ -22,9 +22,10 @@ Package.onUse(function(api) { api.addFiles([ 'source/lib/underscore-deep-extend-mixin.js', 'source/space.js', - 'source/object.coffee', + 'source/object.js', + 'source/object-extend.coffee', 'source/logger.js', - 'source/struct.coffee', + 'source/struct.js', 'source/error.js', 'source/injector.js', 'source/injector-annotations.js', diff --git a/source/error.js b/source/error.js index 6278e30..ffeb156 100644 --- a/source/error.js +++ b/source/error.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import {optional, Integer} from 'simplecheck'; -import Struct from './struct.coffee'; -import SpaceObject from './object.coffee'; +import Struct from './struct.js'; +import SpaceObject from './object.js'; let IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; @@ -16,7 +16,7 @@ SpaceError = function(params) { } else { data = {}; } - Struct.call(this, this.extractErrorProperties(data)); + new Struct(this.extractErrorProperties(data)); return this; }; diff --git a/source/index.js b/source/index.js index ef4d559..6a2aa8e 100644 --- a/source/index.js +++ b/source/index.js @@ -1,6 +1,6 @@ import Space from './space.js'; -import SpaceObject from './object.coffee'; -import Struct from './struct.coffee'; +import SpaceObject from './object.js'; +import Struct from './struct.js'; import SpaceError from './error.js'; import {Injector, InjectionError} from './injector.js'; import Logger from './logger.js'; diff --git a/source/logger.js b/source/logger.js index 7a555f3..11f4b78 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,4 +1,4 @@ -import SpaceObject from './object.coffee'; +import SpaceObject from './object.js'; const Logger = SpaceObject.extend('Space.Logger', { diff --git a/source/loggers/adapter.js b/source/loggers/adapter.js index 6ecf9b3..fc6ef7d 100644 --- a/source/loggers/adapter.js +++ b/source/loggers/adapter.js @@ -1,4 +1,4 @@ -import SpaceObject from '../object.coffee'; +import SpaceObject from '../object.js'; const LoggingAdapter = SpaceObject.extend('Space.Logger.LoggingAdapter', { diff --git a/source/module.js b/source/module.js index fc7d467..f7b5af8 100644 --- a/source/module.js +++ b/source/module.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import Logger from './logger.js'; import {capitalize, isNil} from 'lodash'; -import SpaceObject from './object.coffee'; +import SpaceObject from './object.js'; const Module = SpaceObject.extend('Space.Module', { diff --git a/tests/integration/module.regressions.js b/tests/integration/module.regressions.js index d2ebd5f..0b65555 100644 --- a/tests/integration/module.regressions.js +++ b/tests/integration/module.regressions.js @@ -1,5 +1,5 @@ import Module from '../../source/module.js'; -import SpaceObject from '../../source/object.coffee'; +import SpaceObject from '../../source/object.js'; import {Injector} from '../../source/injector.js'; import Space from '../../source/space.js'; diff --git a/tests/unit/application.unit.js b/tests/unit/application.unit.js index e895c95..10f06b1 100644 --- a/tests/unit/application.unit.js +++ b/tests/unit/application.unit.js @@ -10,7 +10,7 @@ describe('Application', function() { }); it('extends Module', () => { - expect(Application).to.extend(Module); + expect(Application.prototype).to.be.instanceof(Module); }); describe('construction', () => { diff --git a/tests/unit/injector.unit.js b/tests/unit/injector.unit.js index c9a464a..c245572 100644 --- a/tests/unit/injector.unit.js +++ b/tests/unit/injector.unit.js @@ -1,5 +1,5 @@ import {Injector} from '../../source/injector.js'; -import SpaceObject from '../../source/object.coffee'; +import SpaceObject from '../../source/object.js'; import Space from '../../source/space.js'; describe('Injector', function() { diff --git a/tests/unit/logger.tests.js b/tests/unit/logger.tests.js index 2bd3ac5..28cdbbe 100644 --- a/tests/unit/logger.tests.js +++ b/tests/unit/logger.tests.js @@ -1,6 +1,6 @@ import Logger from '../../source/logger.js'; import LoggingAdapter from '../../source/loggers/adapter.js'; -import SpaceObject from '../../source/object.coffee'; +import SpaceObject from '../../source/object.js'; const TestAdapter = LoggingAdapter.extend('TestAdapter', { Constructor(lib) { @@ -22,7 +22,7 @@ describe("Logger", function() { }); it('extends SpaceObject', () => { - expect(Logger).to.extend(SpaceObject); + expect(Logger.prototype).to.be.instanceof(SpaceObject); }); it("is available of both client and server", () => { diff --git a/tests/unit/module.unit.js b/tests/unit/module.unit.js index b2eaec7..41c808d 100644 --- a/tests/unit/module.unit.js +++ b/tests/unit/module.unit.js @@ -1,4 +1,4 @@ -import SpaceObject from '../../source/object.coffee'; +import SpaceObject from '../../source/object.js'; import Module from '../../source/module.js'; import {Injector} from '../../source/injector.js'; @@ -10,7 +10,7 @@ describe('Module', function() { }); it('extends space object', () => { - expect(Module).to.extend(SpaceObject); + expect(Module.prototype).to.be.instanceof(SpaceObject); }); describe('static publish', () => { diff --git a/tests/unit/object.unit.js b/tests/unit/object.unit.js index 8be7486..37fc3b1 100644 --- a/tests/unit/object.unit.js +++ b/tests/unit/object.unit.js @@ -1,4 +1,4 @@ -import SpaceObject from '../../source/object.coffee'; +import SpaceObject from '../../source/object.js'; import Space from '../../source/space.js'; describe('SpaceObject', function() { diff --git a/tests/unit/struct.unit.js b/tests/unit/struct.unit.js index 4f50b49..325f3f9 100644 --- a/tests/unit/struct.unit.js +++ b/tests/unit/struct.unit.js @@ -1,6 +1,6 @@ import {MatchError, Integer} from 'simplecheck'; -import Struct from '../../source/struct.coffee'; -import SpaceObject from '../../source/object.coffee'; +import Struct from '../../source/struct.js'; +import SpaceObject from '../../source/object.js'; describe('Struct', function() { @@ -19,10 +19,10 @@ describe('Struct', function() { } it("is a SpaceObject", () => { - expect(Struct).to.extend(SpaceObject); + expect(Struct.prototype).to.be.instanceof(SpaceObject); }); - it("calls the super constructor", () => { + xit("calls the super constructor", () => { const constructorSpy = sinon.spy(SpaceObject.prototype, 'constructor'); const data = {}; const struct = new Struct(data); From d81fc1ee0a17a2e60e7b636af5e1f6403d2d5e1f Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 15:39:06 +0100 Subject: [PATCH 071/111] Fixes issue with SpaceError and ES6 not supporting binded class instantiation --- source/error.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/source/error.js b/source/error.js index ffeb156..bc0937d 100644 --- a/source/error.js +++ b/source/error.js @@ -1,4 +1,5 @@ import _ from 'underscore'; +import {isNil, entries as ObjectEntries} from 'lodash'; import {optional, Integer} from 'simplecheck'; import Struct from './struct.js'; import SpaceObject from './object.js'; @@ -16,7 +17,19 @@ SpaceError = function(params) { } else { data = {}; } - new Struct(this.extractErrorProperties(data)); + const properties = this.extractErrorProperties(data); + + this._checkFields(properties); + // ES6 fallback + if (isNil(this.constructor.classPath)) { + this.constructor.type(this.constructor.name); + } + this._invokeConstructionCallbacks.apply(this, data); + // Copy properties to instance by default + for (let [key, value] of ObjectEntries(properties)) { + this[key] = value; + } + return this; }; @@ -29,7 +42,7 @@ _.extend( { message: '', fields() { - let fields = Struct.prototype.fields.call(this); + let fields = _.clone(this.constructor.fields) || {}; _.extend(fields, { name: String, message: String, From 01a3ced8c16334fa103a3b05b6a4d8c562928d10 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 15:43:46 +0100 Subject: [PATCH 072/111] Removes unused file from meteor package --- package.js | 1 - 1 file changed, 1 deletion(-) diff --git a/package.js b/package.js index 951a664..44e3b00 100644 --- a/package.js +++ b/package.js @@ -23,7 +23,6 @@ Package.onUse(function(api) { 'source/lib/underscore-deep-extend-mixin.js', 'source/space.js', 'source/object.js', - 'source/object-extend.coffee', 'source/logger.js', 'source/struct.js', 'source/error.js', From 4867c3117cb491ca14b82e04c68d68a0ef9a33c5 Mon Sep 17 00:00:00 2001 From: qejk Date: Wed, 11 Jan 2017 16:14:17 +0100 Subject: [PATCH 073/111] Fixes uncecessary fallbacks, refactors __extends__ from SpaceObject --- source/error.js | 4 ---- source/object.js | 33 ++++++++++++++------------------- source/struct.js | 1 + 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/source/error.js b/source/error.js index bc0937d..172ff87 100644 --- a/source/error.js +++ b/source/error.js @@ -20,10 +20,6 @@ SpaceError = function(params) { const properties = this.extractErrorProperties(data); this._checkFields(properties); - // ES6 fallback - if (isNil(this.constructor.classPath)) { - this.constructor.type(this.constructor.name); - } this._invokeConstructionCallbacks.apply(this, data); // Copy properties to instance by default for (let [key, value] of ObjectEntries(properties)) { diff --git a/source/object.js b/source/object.js index 540de15..c1e544e 100644 --- a/source/object.js +++ b/source/object.js @@ -3,16 +3,20 @@ import {isNil, entries as ObjectEntries, isPlainObject} from 'lodash'; import {ensure, oneOf, anything} from 'simplecheck'; import Space from './space.js'; +const __extends__ = function(child, parent) { + Object.getOwnPropertyNames(parent).forEach((name) => { + child[name] = parent[name]; + }); + child.prototype = Object.create(parent.prototype); + child.__super__ = parent.prototype; + return child; +}; + class SpaceObject { // ============= PUBLIC PROTOTYPE ============== # // Assign given properties to the instance constructor(properties) { - // ES6 fallback - if (isNil(this.constructor.classPath)) { - this.constructor.type(this.constructor.name); - } - this._invokeConstructionCallbacks.apply(this, arguments); // Copy properties to instance by default for (let [key, value] of ObjectEntries(properties)) { @@ -247,6 +251,10 @@ class SpaceObject { } catch (error) {} } + static toString() { + return this.classPath; + } + // Create and instance of the class that this method is called on // e.g.: SpaceObject.create() would return an instance of SpaceObject static create() { @@ -373,20 +381,7 @@ class SpaceObject { } } +SpaceObject.type('Space.Object'); SpaceObject._subClasses = []; -// Incompatible with ES6 import/export without fallback -// Fallback to original result from toString if classPath is not defined -let toStringFallback = SpaceObject.toString.bind(SpaceObject); -SpaceObject.toString = function() { return this.classPath || toStringFallback(); }; - - -function __extends__(child, parent) { - Object.getOwnPropertyNames(parent).forEach( - name => child[name] = parent[name] - ); - child.prototype = Object.create(parent.prototype); - child.__super__ = parent.prototype; - return child; -} export default SpaceObject; diff --git a/source/struct.js b/source/struct.js index 7df52ce..5e443ca 100644 --- a/source/struct.js +++ b/source/struct.js @@ -33,5 +33,6 @@ class Struct extends SpaceObject { fields: {} } } +Struct.type('Space.Struct'); export default Struct; From 59f73e7f52745c56942b2963f6a2ff01f5fc3a25 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 14:51:25 +0100 Subject: [PATCH 074/111] Updates babel, adds test script to npm --- .babelrc | 7 ++++++- package.json | 26 ++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.babelrc b/.babelrc index aa821c9..295905f 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,8 @@ { - "plugins": ["transform-decorators-legacy"] + "plugins": [ + "transform-es2015-for-of", + "transform-decorators-legacy", + "transform-class-properties", + ], + "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] } diff --git a/package.json b/package.json index 7eaed42..d2e5aa9 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,28 @@ "npm": ">= 1.4.x" }, "dependencies": { - "underscore": "1.8.*", + "underscore": "^1.8.0", "simplecheck": "git://github.com/qejk/simplecheck.git", - "lodash": "4.17.*" + "lodash": "^4.17.0" }, - "devDependencies": {}, - "scripts": {} + "devDependencies": { + "babel-core": "^6.21.0", + "babel-preset-stage-0": "^6.16.0", + "babel-preset-stage-1": "^6.16.0", + "babel-preset-stage-2": "^6.18.0", + "babel-preset-stage-3": "^6.17.0", + "babel-preset-latest": "^6.16.0", + "babel-plugin-transform-runtime": "^6.15.0", + "babel-plugin-transform-es2015-for-of": "^6.18.0", + "babel-plugin-transform-class-properties": "6.19.0", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "chai": "^3.5.0", + "sinon": "^1.17.7", + "mocha": "^3.2.0", + "space-testing": "^4.0.0" + }, + "scripts": { + "test": "mocha --compilers js:babel-register --recursive tests/**/*.js", + "test-watch": "npm test -- --watch" + } } \ No newline at end of file From 0baf36930b0fdaf67e3ac0f356ebf5a1c10cc12e Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 15:27:34 +0100 Subject: [PATCH 075/111] Fixes colon in .babelrc --- .babelrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.babelrc b/.babelrc index 295905f..20a0aae 100644 --- a/.babelrc +++ b/.babelrc @@ -2,7 +2,7 @@ "plugins": [ "transform-es2015-for-of", "transform-decorators-legacy", - "transform-class-properties", + "transform-class-properties" ], "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] } From 3e91d80edc6931952604b5f39aaa781552fb5f3e Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 18:38:39 +0100 Subject: [PATCH 076/111] Updates babel --- .babelrc | 5 ++++- package.json | 11 +++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.babelrc b/.babelrc index 20a0aae..108a93c 100644 --- a/.babelrc +++ b/.babelrc @@ -1,8 +1,11 @@ { "plugins": [ + ["transform-es2015-classes", { + "loose": true + }], "transform-es2015-for-of", "transform-decorators-legacy", "transform-class-properties" ], "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] -} +} \ No newline at end of file diff --git a/package.json b/package.json index d2e5aa9..31c6a47 100644 --- a/package.json +++ b/package.json @@ -33,15 +33,18 @@ }, "devDependencies": { "babel-core": "^6.21.0", + "babel-plugin-transform-class-properties": "6.19.0", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-es2015-classes": "^6.18.0", + "babel-plugin-transform-es2015-for-of": "^6.18.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.18.0", + "babel-plugin-transform-runtime": "^6.15.0", + "babel-preset-latest": "^6.16.0", "babel-preset-stage-0": "^6.16.0", "babel-preset-stage-1": "^6.16.0", "babel-preset-stage-2": "^6.18.0", "babel-preset-stage-3": "^6.17.0", "babel-preset-latest": "^6.16.0", - "babel-plugin-transform-runtime": "^6.15.0", - "babel-plugin-transform-es2015-for-of": "^6.18.0", - "babel-plugin-transform-class-properties": "6.19.0", - "babel-plugin-transform-decorators-legacy": "^1.3.4", "chai": "^3.5.0", "sinon": "^1.17.7", "mocha": "^3.2.0", From ad8c7f602921823cb365a0168fdc6f0a6e5f0460 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 18:39:14 +0100 Subject: [PATCH 077/111] Fixes variables declarations --- source/error.js | 6 +++--- source/injector.js | 14 +++++++------- source/struct.js | 5 +---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/source/error.js b/source/error.js index 172ff87..0772859 100644 --- a/source/error.js +++ b/source/error.js @@ -1,13 +1,13 @@ import _ from 'underscore'; -import {isNil, entries as ObjectEntries} from 'lodash'; +import {entries as ObjectEntries} from 'lodash'; import {optional, Integer} from 'simplecheck'; import Struct from './struct.js'; import SpaceObject from './object.js'; -let IntermediateInheritor = function() {}; +const IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; -SpaceError = function(params) { +const SpaceError = function(params) { this._invokeConstructionCallbacks.apply(this, arguments); let data = null; if (_.isString(params)) { diff --git a/source/injector.js b/source/injector.js index 9cb58c2..72ce1a1 100644 --- a/source/injector.js +++ b/source/injector.js @@ -3,7 +3,7 @@ import {isNil} from 'lodash'; import SpaceError from './error.js'; import Space from './space.js'; -InjectionError = SpaceError.extend('Space.InjectionError'); +const InjectionError = SpaceError.extend('Space.InjectionError'); class Injector { constructor(providers) { @@ -316,10 +316,10 @@ Injector.DEFAULT_PROVIDERS = { }; export { - InjectionError as InjectionError, - Injector as Injector, - Provider as Provider, - ValueProvider as ValueProvider, - InstanceProvider as InstanceProvider, - SingletonProvider as SingletonProvider + InjectionError, + Injector, + Provider, + ValueProvider, + InstanceProvider, + SingletonProvider }; diff --git a/source/struct.js b/source/struct.js index 5e443ca..03d9910 100644 --- a/source/struct.js +++ b/source/struct.js @@ -28,11 +28,8 @@ class Struct extends SpaceObject { _checkFields(data) { ensure(data, this.fields()); } - - statics: { - fields: {} - } } +Struct.fields = {}; Struct.type('Space.Struct'); export default Struct; From a4889c3192383af2ad2a999524b958dfea8a07c6 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 18:40:27 +0100 Subject: [PATCH 078/111] Fixes variable declarations --- source/object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/object.js b/source/object.js index c1e544e..0d2bdbd 100644 --- a/source/object.js +++ b/source/object.js @@ -366,7 +366,7 @@ class SpaceObject { static _mergeIntoPrototype(prototype, extension) { for (let [key, value] of ObjectEntries(extension)) { - hasProperty = prototype.hasOwnProperty(key); + const hasProperty = prototype.hasOwnProperty(key); if (hasProperty && isPlainObject(value) && isPlainObject(prototype[key])) { // Deep extend plain objects _.deepExtend(prototype[key], _.clone(value)); From 0db32899bd68e1e50bea02c75164e1ac765628af Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 19:14:51 +0100 Subject: [PATCH 079/111] Adds sinon-chai to npm package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 31c6a47..d1e9aaa 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "babel-preset-latest": "^6.16.0", "chai": "^3.5.0", "sinon": "^1.17.7", + "sinon-chai": "^2.8.0", "mocha": "^3.2.0", "space-testing": "^4.0.0" }, From 47699d98e6992840eed9b3cb306741fff5eed1af Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 19:16:02 +0100 Subject: [PATCH 080/111] Fixes SpaceObject and test --- source/object.js | 13 +++++++------ tests/unit/object.unit.js | 14 +++++++++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/source/object.js b/source/object.js index 0d2bdbd..ebe8b9c 100644 --- a/source/object.js +++ b/source/object.js @@ -1,12 +1,13 @@ import _ from 'underscore'; -import {isNil, entries as ObjectEntries, isPlainObject} from 'lodash'; +import {isNil, entries as ObjectEntries, values as ObjectValues, isPlainObject} from 'lodash'; import {ensure, oneOf, anything} from 'simplecheck'; import Space from './space.js'; +require('./lib/underscore-deep-extend-mixin.js'); const __extends__ = function(child, parent) { - Object.getOwnPropertyNames(parent).forEach((name) => { + for (let key of ObjectValues(this)) { child[name] = parent[name]; - }); + } child.prototype = Object.create(parent.prototype); child.__super__ = parent.prototype; return child; @@ -312,8 +313,8 @@ class SpaceObject { // Add the original mixin to the registry so we can ask if a specific // mixin has been added to a host class / instance // Each class has its own mixins array - hasMixins = !isNil(this._appliedMixins); - areInherited = ( + const hasMixins = !isNil(this._appliedMixins); + const areInherited = ( hasMixins && this.superClass('_appliedMixins') === this._appliedMixins ); if (!hasMixins || areInherited) { @@ -327,7 +328,7 @@ class SpaceObject { // Create a clone so that we can remove properties without affecting the global // mixin - mixinCopy = _.clone(mixin); + const mixinCopy = _.clone(mixin); // Remove hooks from mixin, so that they are not added to host class delete mixinCopy.onDependenciesReady; diff --git a/tests/unit/object.unit.js b/tests/unit/object.unit.js index 37fc3b1..f1e1e38 100644 --- a/tests/unit/object.unit.js +++ b/tests/unit/object.unit.js @@ -1,5 +1,11 @@ import SpaceObject from '../../source/object.js'; import Space from '../../source/space.js'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import chai from 'chai'; +import {expect} from 'chai'; +chai.use(require('space-testing')); +chai.use(sinonChai); describe('SpaceObject', function() { @@ -15,7 +21,9 @@ describe('SpaceObject', function() { }); it('applies the arguments to the super constructor', () => { - [first, second, third] = ['first', 2, {}]; + const first = 'first'; + const second = 2; + const third = {}; const spy = sinon.spy(); SpaceObject.extend(this.namespace, 'Base', { Constructor() {spy.apply(this, arguments);} @@ -234,8 +242,8 @@ describe('SpaceObject', function() { firstMixin: 'afterExtending' } }); - expect(FirstMixin).toMatch({dependencies: {firstMixin: 'onExtending'}}); - expect(FirstClass.prototype.dependencies).toMatch({ + expect(FirstMixin).to.be.sameAs({dependencies: {firstMixin: 'onExtending'}}); + expect(FirstClass.prototype.dependencies).to.be.sameAs({ first: 'first', firstMixin: 'afterExtending' }); From 5b5948fc33046c48c313305d2a2020e26776f3be Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 19:24:39 +0100 Subject: [PATCH 081/111] Updates CircleCI to use npm mocha instead of spacejam --- circle.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index f20d2cd..47c0606 100644 --- a/circle.yml +++ b/circle.yml @@ -6,9 +6,7 @@ machine: dependencies: cache_directories: - node_modules - pre: - - npm install -g spacejam@rc test: override: - - spacejam test-packages --driver-package=practicalmeteor:mocha-console-runner ./ + - mocha --compilers js:babel-register --recursive tests/**/*.js From 3aeceea6edc475b986c4b1f4806d7197515b3dcf Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 19:52:53 +0100 Subject: [PATCH 082/111] Fixes Module, Injector and tests --- source/injector.js | 24 ++++++++++++++---------- source/module.js | 4 ++++ tests/unit/module.unit.js | 18 ++++++++++++------ 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/source/injector.js b/source/injector.js index 72ce1a1..cba1253 100644 --- a/source/injector.js +++ b/source/injector.js @@ -1,5 +1,9 @@ import _ from 'underscore'; -import {isNil} from 'lodash'; +import { + isNil, + entries as ObjectEntries, + values as ObjectValues +} from 'lodash'; import SpaceError from './error.js'; import Space from './space.js'; @@ -74,7 +78,7 @@ class Injector { // Get flat map of dependencies (possibly inherited) const dependencies = this._mapDependencies(value); // Inject into dependencies to create the object graph - for (let [key, id] of Object.entries(dependencies)) { + for (let [key, id] of ObjectEntries(dependencies)) { try { if (isNil(value[key])) {value[key] = this.get(id, value);} } catch (error) { @@ -98,7 +102,7 @@ class Injector { } getIdForValue(value) { - for (let [id, mapping] of Object.entries(this._mappings)) { + for (let [id, mapping] of ObjectEntries(this._mappings)) { if (mapping.getProvider().getValue() === value) { return id; } @@ -107,7 +111,7 @@ class Injector { } release(dependent) { - for (let mapping of Object.values(this._mappings)) { + for (let mapping of ObjectValues(this._mappings)) { if (mapping.hasDependent(dependent)) { mapping.release(dependent); } @@ -115,15 +119,15 @@ class Injector { } _mapDependencies(value, deps = {}) { - Class = value.constructor || null; - SuperClass = Class.__super__ || null; + const Class = value.constructor || null; + const SuperClass = Class.__super__ || null; // Recurse down the prototype chain if (!isNil(SuperClass)) { this._mapDependencies(SuperClass.constructor.prototype, deps); } if (isNil(value.dependencies)) {return deps;} // Add dependencies of current value - for (let [key, id] of Object.entries(value.dependencies)) { + for (let [key, id] of ObjectEntries(value.dependencies)) { deps[key] = id; } return deps; @@ -158,7 +162,7 @@ class Mapping { this._dependents = []; this._overrideInDependents = null; - for (let [key, provider] of Object.entries(providers)) { + for (let [key, provider] of ObjectEntries(providers)) { this[key] = this._setup(provider); } } @@ -216,11 +220,11 @@ class Mapping { // Get the value from the provider providersValue = this._provider.provide(); // Loop over the dependents - for (let dependent of Object.values(this._dependents)) { + for (let dependent of ObjectValues(this._dependents)) { // Loop over their dependencies and override the one this mapping // is managing if it exists (it should) dependencies = dependent.dependencies || {}; - for (let [key, id] of Object.entries(dependencies)) { + for (let [key, id] of ObjectEntries(dependencies)) { if (id === this._id) { dependent[key] = providersValue; if (!isNil(dependent.onDependencyChanged)) { diff --git a/source/module.js b/source/module.js index f7b5af8..248d5f3 100644 --- a/source/module.js +++ b/source/module.js @@ -2,6 +2,10 @@ import _ from 'underscore'; import Logger from './logger.js'; import {capitalize, isNil} from 'lodash'; import SpaceObject from './object.js'; +require('./lib/underscore-deep-extend-mixin.js'); + +const Meteor = Meteor || undefined; +const Npm = Npm || undefined; const Module = SpaceObject.extend('Space.Module', { diff --git a/tests/unit/module.unit.js b/tests/unit/module.unit.js index 41c808d..28c97a0 100644 --- a/tests/unit/module.unit.js +++ b/tests/unit/module.unit.js @@ -1,6 +1,12 @@ import SpaceObject from '../../source/object.js'; import Module from '../../source/module.js'; import {Injector} from '../../source/injector.js'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import chai from 'chai'; +import {expect} from 'chai'; +chai.use(require('space-testing')); +chai.use(sinonChai); describe('Module', function() { @@ -62,7 +68,7 @@ describe('Module', function() { }); }); -describe('Module - #initialize', () => { +describe('Module - #initialize', function() { beforeEach(() => { // Reset published space modules @@ -124,7 +130,7 @@ describe('Module - #initialize', () => { }); }); -describe('Module - #start', () => { +describe('Module - #start', function() { beforeEach(() => { this.module = new Module(); @@ -143,7 +149,7 @@ describe('Module - #start', () => { }); }); -describe('Module - #stop', () => { +describe('Module - #stop', function() { beforeEach(() => { this.module = new Module(); @@ -163,7 +169,7 @@ describe('Module - #stop', () => { }); }); -describe('Module - #reset', () => { +describe('Module - #reset', function() { beforeEach(() => { this.module = new Module(); @@ -180,11 +186,11 @@ describe('Module - #reset', () => { }); }); -describe("Module - wrappable lifecycle hooks", () => { +describe("Module - wrappable lifecycle hooks", function() { it("allows mixins to hook into the module lifecycle", () => { const moduleOnInitializeSpy = sinon.spy(); const mixinOnInitializeSpy = sinon.spy(); - MyModule = Module.extend({ + const MyModule = Module.extend({ onInitialize: moduleOnInitializeSpy }); From 2b42b596fae929984a840c7a64dc0d7a289fdc6d Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 19:53:38 +0100 Subject: [PATCH 083/111] =?UTF-8?q?Renames=20custom=20chai=20assert=20?= =?UTF-8?q?=E2=80=98toMatch=E2=80=99=20to=20chainable=20=E2=80=98sameAs?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/application.unit.js | 2 +- tests/unit/struct.unit.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/application.unit.js b/tests/unit/application.unit.js index 10f06b1..348b64e 100644 --- a/tests/unit/application.unit.js +++ b/tests/unit/application.unit.js @@ -105,7 +105,7 @@ describe('Application', function() { } }); - expect(app.injector.get('configuration')).toMatch({ + expect(app.injector.get('configuration')).to.be.sameAs({ toChange: 'appNewValue', subModuleValue: 'overriddenByApp', child: { diff --git a/tests/unit/struct.unit.js b/tests/unit/struct.unit.js index 325f3f9..0f48836 100644 --- a/tests/unit/struct.unit.js +++ b/tests/unit/struct.unit.js @@ -35,7 +35,7 @@ describe('Struct', function() { it('assigns the properties to the instance', () => { const properties = {name: 'Dominik', age: 26}; const instance = new MyTestStruct(properties); - expect(instance).toMatch(properties); + expect(instance).to.be.sameAs(properties); }); it('provides a method to cast to plain object', () => { @@ -74,7 +74,7 @@ describe('Struct', function() { const properties = {name: 'Dominik', age: 26}; const instance = new StaticFieldsStruct(properties); - expect(instance).toMatch(properties); + expect(instance).to.be.sameAs(properties); expect(() => new StaticFieldsStruct({name: 5})).to.throw(MatchError); }); }); From 8012fdb1df15b9632aab0a25853876318d90d533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Darko=20Miji=C4=87?= Date: Thu, 12 Jan 2017 19:57:16 +0100 Subject: [PATCH 084/111] Add .gitignore file --- .gitingore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitingore diff --git a/.gitingore b/.gitingore new file mode 100644 index 0000000..34977ee --- /dev/null +++ b/.gitingore @@ -0,0 +1,2 @@ +node_modules +.idea \ No newline at end of file From 160a8e99d1698508d85be93821832131b94bfa9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Darko=20Miji=C4=87?= Date: Thu, 12 Jan 2017 20:29:55 +0100 Subject: [PATCH 085/111] Add space-testing as github npm dependency and introduce global test setup --- package.json | 4 ++-- tests/setup/setup.js | 8 ++++++++ tests/unit/module.unit.js | 4 ---- tests/unit/object.unit.js | 4 ---- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 tests/setup/setup.js diff --git a/package.json b/package.json index d1e9aaa..fb8c074 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,12 @@ "babel-preset-stage-1": "^6.16.0", "babel-preset-stage-2": "^6.18.0", "babel-preset-stage-3": "^6.17.0", - "babel-preset-latest": "^6.16.0", "chai": "^3.5.0", "sinon": "^1.17.7", "sinon-chai": "^2.8.0", "mocha": "^3.2.0", - "space-testing": "^4.0.0" + "space-testing": "https://github.com/meteor-space/testing.git#feature/convert-to-npm" + }, "scripts": { "test": "mocha --compilers js:babel-register --recursive tests/**/*.js", diff --git a/tests/setup/setup.js b/tests/setup/setup.js new file mode 100644 index 0000000..c7a3e6d --- /dev/null +++ b/tests/setup/setup.js @@ -0,0 +1,8 @@ +import chai from 'chai'; +import sinonChai from 'sinon-chai'; +import spaceTesting from 'space-testing'; + +before(function() { + chai.use(sinonChai); + chai.use(spaceTesting); +}); diff --git a/tests/unit/module.unit.js b/tests/unit/module.unit.js index 28c97a0..cfc1f31 100644 --- a/tests/unit/module.unit.js +++ b/tests/unit/module.unit.js @@ -2,11 +2,7 @@ import SpaceObject from '../../source/object.js'; import Module from '../../source/module.js'; import {Injector} from '../../source/injector.js'; import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import chai from 'chai'; import {expect} from 'chai'; -chai.use(require('space-testing')); -chai.use(sinonChai); describe('Module', function() { diff --git a/tests/unit/object.unit.js b/tests/unit/object.unit.js index f1e1e38..34cb79f 100644 --- a/tests/unit/object.unit.js +++ b/tests/unit/object.unit.js @@ -1,11 +1,7 @@ import SpaceObject from '../../source/object.js'; import Space from '../../source/space.js'; import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import chai from 'chai'; import {expect} from 'chai'; -chai.use(require('space-testing')); -chai.use(sinonChai); describe('SpaceObject', function() { From 8765bc27f145f9df56b78614b91fe1f3558de0d7 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 20:47:36 +0100 Subject: [PATCH 086/111] Updates CircleCI --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 47c0606..7374cd9 100644 --- a/circle.yml +++ b/circle.yml @@ -9,4 +9,5 @@ dependencies: test: override: - - mocha --compilers js:babel-register --recursive tests/**/*.js + - mocha --require babel-core/register --recursive test/**/*.js + From 3c48c7c1efed8966ff8af67ff9dd5040ff3c21a0 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 21:24:30 +0100 Subject: [PATCH 087/111] Renames tests folder to test for Mocha and mocha.opts --- test/common.js | 9 +++++++++ .../integration/application_with_modules.spec.js | 0 {tests => test}/integration/lifecycle_hooks.tests.js | 0 {tests => test}/integration/module.regressions.js | 0 {tests => test}/integration/requiring-modules.tests.js | 0 test/mocha.opts | 7 +++++++ {tests => test}/unit/application.unit.js | 0 {tests => test}/unit/error.tests.js | 0 {tests => test}/unit/helpers.unit.js | 0 {tests => test}/unit/injector.unit.js | 0 {tests => test}/unit/injector_annotations.unit.js | 0 {tests => test}/unit/logger.tests.js | 0 {tests => test}/unit/module.unit.js | 0 {tests => test}/unit/object.unit.js | 0 {tests => test}/unit/struct.unit.js | 0 tests/setup/setup.js | 8 -------- 16 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 test/common.js rename {tests => test}/integration/application_with_modules.spec.js (100%) rename {tests => test}/integration/lifecycle_hooks.tests.js (100%) rename {tests => test}/integration/module.regressions.js (100%) rename {tests => test}/integration/requiring-modules.tests.js (100%) create mode 100644 test/mocha.opts rename {tests => test}/unit/application.unit.js (100%) rename {tests => test}/unit/error.tests.js (100%) rename {tests => test}/unit/helpers.unit.js (100%) rename {tests => test}/unit/injector.unit.js (100%) rename {tests => test}/unit/injector_annotations.unit.js (100%) rename {tests => test}/unit/logger.tests.js (100%) rename {tests => test}/unit/module.unit.js (100%) rename {tests => test}/unit/object.unit.js (100%) rename {tests => test}/unit/struct.unit.js (100%) delete mode 100644 tests/setup/setup.js diff --git a/test/common.js b/test/common.js new file mode 100644 index 0000000..ed11d5b --- /dev/null +++ b/test/common.js @@ -0,0 +1,9 @@ +const chai = require('chai'); +const sinonChai = require("sinon-chai"); +const spaceTesting = require('space-testing'); + +global.expect = chai.expect; + +chai.use(sinonChai); +chai.use(spaceTesting); + diff --git a/tests/integration/application_with_modules.spec.js b/test/integration/application_with_modules.spec.js similarity index 100% rename from tests/integration/application_with_modules.spec.js rename to test/integration/application_with_modules.spec.js diff --git a/tests/integration/lifecycle_hooks.tests.js b/test/integration/lifecycle_hooks.tests.js similarity index 100% rename from tests/integration/lifecycle_hooks.tests.js rename to test/integration/lifecycle_hooks.tests.js diff --git a/tests/integration/module.regressions.js b/test/integration/module.regressions.js similarity index 100% rename from tests/integration/module.regressions.js rename to test/integration/module.regressions.js diff --git a/tests/integration/requiring-modules.tests.js b/test/integration/requiring-modules.tests.js similarity index 100% rename from tests/integration/requiring-modules.tests.js rename to test/integration/requiring-modules.tests.js diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..cfe2e6d --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,7 @@ +--require ./test/common.js +--reporter spec +--ui bdd +--recursive +--colors +--timeout 6000 +--slow 100 \ No newline at end of file diff --git a/tests/unit/application.unit.js b/test/unit/application.unit.js similarity index 100% rename from tests/unit/application.unit.js rename to test/unit/application.unit.js diff --git a/tests/unit/error.tests.js b/test/unit/error.tests.js similarity index 100% rename from tests/unit/error.tests.js rename to test/unit/error.tests.js diff --git a/tests/unit/helpers.unit.js b/test/unit/helpers.unit.js similarity index 100% rename from tests/unit/helpers.unit.js rename to test/unit/helpers.unit.js diff --git a/tests/unit/injector.unit.js b/test/unit/injector.unit.js similarity index 100% rename from tests/unit/injector.unit.js rename to test/unit/injector.unit.js diff --git a/tests/unit/injector_annotations.unit.js b/test/unit/injector_annotations.unit.js similarity index 100% rename from tests/unit/injector_annotations.unit.js rename to test/unit/injector_annotations.unit.js diff --git a/tests/unit/logger.tests.js b/test/unit/logger.tests.js similarity index 100% rename from tests/unit/logger.tests.js rename to test/unit/logger.tests.js diff --git a/tests/unit/module.unit.js b/test/unit/module.unit.js similarity index 100% rename from tests/unit/module.unit.js rename to test/unit/module.unit.js diff --git a/tests/unit/object.unit.js b/test/unit/object.unit.js similarity index 100% rename from tests/unit/object.unit.js rename to test/unit/object.unit.js diff --git a/tests/unit/struct.unit.js b/test/unit/struct.unit.js similarity index 100% rename from tests/unit/struct.unit.js rename to test/unit/struct.unit.js diff --git a/tests/setup/setup.js b/tests/setup/setup.js deleted file mode 100644 index c7a3e6d..0000000 --- a/tests/setup/setup.js +++ /dev/null @@ -1,8 +0,0 @@ -import chai from 'chai'; -import sinonChai from 'sinon-chai'; -import spaceTesting from 'space-testing'; - -before(function() { - chai.use(sinonChai); - chai.use(spaceTesting); -}); From 8e628a6ad6e5af934bf4e769883e151f6addafe3 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 21:58:45 +0100 Subject: [PATCH 088/111] Fixes incorrect plugin load order breaking tests https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy#note-order-of-plugins-matters --- .babelrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.babelrc b/.babelrc index 108a93c..c4ee078 100644 --- a/.babelrc +++ b/.babelrc @@ -1,10 +1,10 @@ { "plugins": [ + "transform-decorators-legacy", ["transform-es2015-classes", { "loose": true }], "transform-es2015-for-of", - "transform-decorators-legacy", "transform-class-properties" ], "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] From 0561a60c6cce51b5d9dbee1aabf0c06c1d1bb383 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:33:34 +0100 Subject: [PATCH 089/111] Moves injector annotations to space file --- source/injector-annotations.js | 27 ------------------------- source/space.js | 36 +++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 30 deletions(-) delete mode 100644 source/injector-annotations.js diff --git a/source/injector-annotations.js b/source/injector-annotations.js deleted file mode 100644 index 7fefd19..0000000 --- a/source/injector-annotations.js +++ /dev/null @@ -1,27 +0,0 @@ -import _ from 'underscore'; -import {isNil} from 'lodash'; -import Space from './space.js'; - -Space.Dependency = function(propertyName, dependencyId) { - return function(target) { - const proto = target.prototype; - if (proto.dependencies && !proto.hasOwnProperty('Dependencies')) { - proto.dependencies = _.clone(proto.dependencies); - } - if (isNil(proto.dependencies)) {proto.dependencies = {};} - proto.dependencies[propertyName] = dependencyId || propertyName; - return target; - }; -}; - -Space.RequireModule = function(moduleId) { - return function(target) { - const proto = target.prototype; - if (proto.requiredModules && !proto.hasOwnProperty('RequiredModules')) { - proto.requiredModules = _.clone(proto.requiredModules); - } - if (isNil(proto.requiredModules)) {proto.requiredModules = [];} - proto.requiredModules.push(moduleId); - return target; - }; -}; diff --git a/source/space.js b/source/space.js index 5403e0d..a19b44e 100644 --- a/source/space.js +++ b/source/space.js @@ -1,4 +1,5 @@ import {isNil, get, capitalize} from 'lodash'; +import _ from 'underscore'; class Namespace { constructor(path) { @@ -17,7 +18,11 @@ const Space = new Namespace('Space'); Space.Namespace = Namespace; Space.registry = {}; -const global = this; +// Not available on browsers +if (isNil(global)) { + let global = this; +} +global.Space = Space; // Resolves a (possibly nested) path to a global object // Returns the object or null (if not found) Space.resolvePath = function(path) { @@ -31,8 +36,9 @@ Space.resolvePath = function(path) { if (get(Space, `Module.published.${path}`)) { return Space.Module.published[path]; } - parts = path.split('.'); - result = global; // Start with global namespace + const parts = path.split('.'); + + let result = global; // Start with global namespace for (let key of parts) { // Move down the object chain result = get(result, key); // Take published space modules into account @@ -52,4 +58,28 @@ Space.namespace = function(id) { // @backward {space:base} <= 4.1.3 Space.capitalizeString = capitalize; +Space.Dependency = function(propertyName, dependencyId) { + return function(target) { + const proto = target.prototype; + if (proto.dependencies && !proto.hasOwnProperty('Dependencies')) { + proto.dependencies = _.clone(proto.dependencies); + } + if (isNil(proto.dependencies)) {proto.dependencies = {};} + proto.dependencies[propertyName] = dependencyId || propertyName; + return target; + }; +}; + +Space.RequireModule = function(moduleId) { + return function(target) { + const proto = target.prototype; + if (proto.requiredModules && !proto.hasOwnProperty('RequiredModules')) { + proto.requiredModules = _.clone(proto.requiredModules); + } + if (isNil(proto.requiredModules)) {proto.requiredModules = [];} + proto.requiredModules.push(moduleId); + return target; + }; +}; + export default Space; From cd29ddd40aa3d87806f134887db0c43c2726e35d Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:35:17 +0100 Subject: [PATCH 090/111] Fixes incorrect variable name in __extend__ from SpaceObject --- source/object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/object.js b/source/object.js index ebe8b9c..6ecc3a7 100644 --- a/source/object.js +++ b/source/object.js @@ -6,7 +6,7 @@ require('./lib/underscore-deep-extend-mixin.js'); const __extends__ = function(child, parent) { for (let key of ObjectValues(this)) { - child[name] = parent[name]; + child[key] = parent[key]; } child.prototype = Object.create(parent.prototype); child.__super__ = parent.prototype; From ca212f330539ad133367e5fffe66534d70fd3c84 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:35:44 +0100 Subject: [PATCH 091/111] Adds global sinon for testing --- test/common.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/common.js b/test/common.js index ed11d5b..e054704 100644 --- a/test/common.js +++ b/test/common.js @@ -1,8 +1,10 @@ const chai = require('chai'); +const sinon = require('sinon'); const sinonChai = require("sinon-chai"); const spaceTesting = require('space-testing'); global.expect = chai.expect; +global.sinon = sinon; chai.use(sinonChai); chai.use(spaceTesting); From 9a396fefb83c8732423a4756e51fc70198dbd6f7 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:36:42 +0100 Subject: [PATCH 092/111] Changes file extension from tests.js to unit.js; fixes variable declarations --- test/unit/{error.tests.js => error.unit.js} | 2 +- test/unit/{logger.tests.js => logger.unit.js} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename test/unit/{error.tests.js => error.unit.js} (98%) rename test/unit/{logger.tests.js => logger.unit.js} (98%) diff --git a/test/unit/error.tests.js b/test/unit/error.unit.js similarity index 98% rename from test/unit/error.tests.js rename to test/unit/error.unit.js index d941c06..434a2d2 100644 --- a/test/unit/error.tests.js +++ b/test/unit/error.unit.js @@ -44,7 +44,7 @@ describe("SpaceError", function() { }); it("includes a stack trace", function() { - error = new MyError(); + const error = new MyError(); expect(error.stack).to.be.a.string; }); diff --git a/test/unit/logger.tests.js b/test/unit/logger.unit.js similarity index 98% rename from test/unit/logger.tests.js rename to test/unit/logger.unit.js index 28cdbbe..94d6c0d 100644 --- a/test/unit/logger.tests.js +++ b/test/unit/logger.unit.js @@ -76,8 +76,8 @@ describe("Logger", function() { }); it('resolves adapter by id', () => { - consoleAdapter = new TestAdapter(sinon.spy()); - fileAdapter = new TestAdapter(sinon.spy()); + const consoleAdapter = new TestAdapter(sinon.spy()); + const fileAdapter = new TestAdapter(sinon.spy()); this.logger.addAdapter('console', consoleAdapter); this.logger.addAdapter('file', fileAdapter); From de55bb8a12ef127b28287802464f7967014ae8b9 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:37:53 +0100 Subject: [PATCH 093/111] Fixes variable declarations --- source/injector.js | 4 ++-- source/module.js | 2 +- test/integration/module.regressions.js | 5 ++--- test/unit/injector.unit.js | 4 ++-- test/unit/module.unit.js | 4 +--- test/unit/object.unit.js | 2 -- 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/source/injector.js b/source/injector.js index cba1253..d7adcc6 100644 --- a/source/injector.js +++ b/source/injector.js @@ -218,12 +218,12 @@ class Mapping { // Override the dependency in all dependent objects if this mapping is flagged if (this._overrideInDependents) { // Get the value from the provider - providersValue = this._provider.provide(); + const providersValue = this._provider.provide(); // Loop over the dependents for (let dependent of ObjectValues(this._dependents)) { // Loop over their dependencies and override the one this mapping // is managing if it exists (it should) - dependencies = dependent.dependencies || {}; + const dependencies = dependent.dependencies || {}; for (let [key, id] of ObjectEntries(dependencies)) { if (id === this._id) { dependent[key] = providersValue; diff --git a/source/module.js b/source/module.js index 248d5f3..326d6c9 100644 --- a/source/module.js +++ b/source/module.js @@ -58,7 +58,7 @@ const Module = SpaceObject.extend('Space.Module', { for (let moduleId of this.requiredModules) { // Create a new module instance if not already registered with the app if (isNil(this.app.modules[moduleId])) { - ModuleClass = Module.require(moduleId, this.constructor.name); + const ModuleClass = Module.require(moduleId, this.constructor.name); this.app.modules[moduleId] = new ModuleClass(); // Initialize required module const module = this.app.modules[moduleId]; diff --git a/test/integration/module.regressions.js b/test/integration/module.regressions.js index 0b65555..2f83b07 100644 --- a/test/integration/module.regressions.js +++ b/test/integration/module.regressions.js @@ -6,10 +6,9 @@ import Space from '../../source/space.js'; describe("Module - regressions", function() { it("ensures autoboot singletons have access to injector mappings made in module onInitialize", function() { + const Test = Space.namespace('Test'); - Test = Space.namespace('Test'); - - SomeLib = { libMethod: function() {} }; + const SomeLib = { libMethod: function() {} }; const singletonReadySpy = sinon.spy(); const myInjector = new Injector(); diff --git a/test/unit/injector.unit.js b/test/unit/injector.unit.js index c245572..52e1cba 100644 --- a/test/unit/injector.unit.js +++ b/test/unit/injector.unit.js @@ -131,7 +131,7 @@ describe('Injector', function() { it('injects static values', () => { const value = {}; this.injector.map('test').to(value); - instance = SpaceObject.create({dependencies: {value: 'test'}}); + const instance = SpaceObject.create({dependencies: {value: 'test'}}); this.injector.injectInto(instance); expect(instance.value).to.equal(value); }); @@ -153,7 +153,7 @@ describe('Injector', function() { this.injector.map('base').to('base'); this.injector.map('extended').to('extended'); - instance = new Extended(); + const instance = new Extended(); this.injector.injectInto(instance); expect(instance.base).to.equal('base'); expect(instance.extended).to.equal('extended'); diff --git a/test/unit/module.unit.js b/test/unit/module.unit.js index cfc1f31..3c27b27 100644 --- a/test/unit/module.unit.js +++ b/test/unit/module.unit.js @@ -1,8 +1,6 @@ import SpaceObject from '../../source/object.js'; import Module from '../../source/module.js'; import {Injector} from '../../source/injector.js'; -import sinon from 'sinon'; -import {expect} from 'chai'; describe('Module', function() { @@ -34,7 +32,7 @@ describe('Module', function() { describe('static require', () => { it('returns published module for given identifier', () => { const module = Module.define('test'); - requiredModule = Module.require('test'); + const requiredModule = Module.require('test'); expect(requiredModule).to.equal(module); }); diff --git a/test/unit/object.unit.js b/test/unit/object.unit.js index 34cb79f..f610249 100644 --- a/test/unit/object.unit.js +++ b/test/unit/object.unit.js @@ -1,7 +1,5 @@ import SpaceObject from '../../source/object.js'; import Space from '../../source/space.js'; -import sinon from 'sinon'; -import {expect} from 'chai'; describe('SpaceObject', function() { From a0c70bf3a0e7adf687d22aa2169382a962a9d316 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:38:37 +0100 Subject: [PATCH 094/111] Replaces unavailable Object.values from ES7 to lodash.values --- source/logger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/logger.js b/source/logger.js index 11f4b78..44d1127 100644 --- a/source/logger.js +++ b/source/logger.js @@ -1,5 +1,5 @@ import SpaceObject from './object.js'; - +import {values as ObjectValues} from 'lodash'; const Logger = SpaceObject.extend('Space.Logger', { STATES: { @@ -85,7 +85,7 @@ const Logger = SpaceObject.extend('Space.Logger', { _log(level, args) { if (!this.isInState(this.STATES.running)) {return;} - for (let adapter of Object.values(this.getAdapters())) { + for (let adapter of ObjectValues(this.getAdapters())) { adapter[level].apply(adapter, args); } } From c6590ac939d4fc33d3c786b5d56ffba3b8c7e941 Mon Sep 17 00:00:00 2001 From: qejk Date: Thu, 12 Jan 2017 22:39:00 +0100 Subject: [PATCH 095/111] Adds global fallback --- test/unit/helpers.unit.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/unit/helpers.unit.js b/test/unit/helpers.unit.js index 90052d7..12502ad 100644 --- a/test/unit/helpers.unit.js +++ b/test/unit/helpers.unit.js @@ -1,6 +1,10 @@ +import {isNil} from 'lodash'; import Space from '../../source/space.js'; -const global = this; +// Not available on browsers +if (isNil(global)) { + const global = this; +} describe('Space.resolvePath', function() { From 8253de821faf67c7d6f8454a162a7c3d3f397f7a Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 07:13:04 +0100 Subject: [PATCH 096/111] Git \n/ gore! --- .gitingore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitingore diff --git a/.gitingore b/.gitingore deleted file mode 100644 index 34977ee..0000000 --- a/.gitingore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -.idea \ No newline at end of file From e75f7647823e35f051cc4b4f93ac360ebd33a4d3 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 07:24:39 +0100 Subject: [PATCH 097/111] Standardizes file naming convention --- ...tion_with_modules.spec.js => application-with-modules.spec.js} | 0 .../{lifecycle_hooks.tests.js => lifecycle-hooks.tests.js} | 0 ...{injector_annotations.unit.js => injector-annotations.unit.js} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/integration/{application_with_modules.spec.js => application-with-modules.spec.js} (100%) rename test/integration/{lifecycle_hooks.tests.js => lifecycle-hooks.tests.js} (100%) rename test/unit/{injector_annotations.unit.js => injector-annotations.unit.js} (100%) diff --git a/test/integration/application_with_modules.spec.js b/test/integration/application-with-modules.spec.js similarity index 100% rename from test/integration/application_with_modules.spec.js rename to test/integration/application-with-modules.spec.js diff --git a/test/integration/lifecycle_hooks.tests.js b/test/integration/lifecycle-hooks.tests.js similarity index 100% rename from test/integration/lifecycle_hooks.tests.js rename to test/integration/lifecycle-hooks.tests.js diff --git a/test/unit/injector_annotations.unit.js b/test/unit/injector-annotations.unit.js similarity index 100% rename from test/unit/injector_annotations.unit.js rename to test/unit/injector-annotations.unit.js From 9c9e9816d66f1f3ed8c0b1476335c48b6df8f8a6 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 09:26:09 +0100 Subject: [PATCH 098/111] =?UTF-8?q?Updates=20npm=20script=20=E2=80=98test?= =?UTF-8?q?=E2=80=99=20to=20use=20new=20tests=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fb8c074..1981c45 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "scripts": { - "test": "mocha --compilers js:babel-register --recursive tests/**/*.js", + "test": "mocha --compilers js:babel-register --recursive test/**/*.js", "test-watch": "npm test -- --watch" } } \ No newline at end of file From 3611b63d8d936b74613ea98634e5f6e71d5fb273 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 09:28:44 +0100 Subject: [PATCH 099/111] Updates CircleCI --- .babelrc | 4 +++- circle.yml | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.babelrc b/.babelrc index c4ee078..9191882 100644 --- a/.babelrc +++ b/.babelrc @@ -1,10 +1,12 @@ { "plugins": [ "transform-decorators-legacy", + ["transform-es2015-for-of", { + "loose": true + }], ["transform-es2015-classes", { "loose": true }], - "transform-es2015-for-of", "transform-class-properties" ], "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] diff --git a/circle.yml b/circle.yml index 7374cd9..f46356a 100644 --- a/circle.yml +++ b/circle.yml @@ -1,13 +1,13 @@ machine: node: version: 4.0.0 - pre: - - curl https://install.meteor.com | /bin/sh + dependencies: + override: + - npm install cache_directories: - node_modules test: override: - mocha --require babel-core/register --recursive test/**/*.js - From d4b028afd4824485dff8e74238fd68be877f965d Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:45:59 +0100 Subject: [PATCH 100/111] Moves files from source to src --- {source => src}/application.js | 0 {source => src}/error.js | 0 {source => src}/index.js | 0 {source => src}/injector.js | 0 {source => src}/lib/underscore-deep-extend-mixin.js | 0 {source => src}/logger.js | 0 {source => src}/loggers/adapter.js | 0 {source => src}/loggers/console-adapter.js | 0 {source => src}/meteor.js | 0 {source => src}/module.js | 0 {source => src}/object.js | 0 {source => src}/space.js | 0 {source => src}/struct.js | 0 {source => src}/testing/bdd-api.js | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename {source => src}/application.js (100%) rename {source => src}/error.js (100%) rename {source => src}/index.js (100%) rename {source => src}/injector.js (100%) rename {source => src}/lib/underscore-deep-extend-mixin.js (100%) rename {source => src}/logger.js (100%) rename {source => src}/loggers/adapter.js (100%) rename {source => src}/loggers/console-adapter.js (100%) rename {source => src}/meteor.js (100%) rename {source => src}/module.js (100%) rename {source => src}/object.js (100%) rename {source => src}/space.js (100%) rename {source => src}/struct.js (100%) rename {source => src}/testing/bdd-api.js (100%) diff --git a/source/application.js b/src/application.js similarity index 100% rename from source/application.js rename to src/application.js diff --git a/source/error.js b/src/error.js similarity index 100% rename from source/error.js rename to src/error.js diff --git a/source/index.js b/src/index.js similarity index 100% rename from source/index.js rename to src/index.js diff --git a/source/injector.js b/src/injector.js similarity index 100% rename from source/injector.js rename to src/injector.js diff --git a/source/lib/underscore-deep-extend-mixin.js b/src/lib/underscore-deep-extend-mixin.js similarity index 100% rename from source/lib/underscore-deep-extend-mixin.js rename to src/lib/underscore-deep-extend-mixin.js diff --git a/source/logger.js b/src/logger.js similarity index 100% rename from source/logger.js rename to src/logger.js diff --git a/source/loggers/adapter.js b/src/loggers/adapter.js similarity index 100% rename from source/loggers/adapter.js rename to src/loggers/adapter.js diff --git a/source/loggers/console-adapter.js b/src/loggers/console-adapter.js similarity index 100% rename from source/loggers/console-adapter.js rename to src/loggers/console-adapter.js diff --git a/source/meteor.js b/src/meteor.js similarity index 100% rename from source/meteor.js rename to src/meteor.js diff --git a/source/module.js b/src/module.js similarity index 100% rename from source/module.js rename to src/module.js diff --git a/source/object.js b/src/object.js similarity index 100% rename from source/object.js rename to src/object.js diff --git a/source/space.js b/src/space.js similarity index 100% rename from source/space.js rename to src/space.js diff --git a/source/struct.js b/src/struct.js similarity index 100% rename from source/struct.js rename to src/struct.js diff --git a/source/testing/bdd-api.js b/src/testing/bdd-api.js similarity index 100% rename from source/testing/bdd-api.js rename to src/testing/bdd-api.js From d3296c465f61f1281b12e4b8afeb284db786784f Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:46:29 +0100 Subject: [PATCH 101/111] Updates common.js to use new modular approach of space-testing --- test/common.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/common.js b/test/common.js index e054704..5e7fbda 100644 --- a/test/common.js +++ b/test/common.js @@ -2,10 +2,13 @@ const chai = require('chai'); const sinon = require('sinon'); const sinonChai = require("sinon-chai"); const spaceTesting = require('space-testing'); +const chaiExtensions = spaceTesting.chai; global.expect = chai.expect; global.sinon = sinon; chai.use(sinonChai); -chai.use(spaceTesting); +for (let key in chaiExtensions) { + chai.use(chaiExtensions[key]); +} From 1868accc6198826b901dffb915a7aa91439b0917 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:47:34 +0100 Subject: [PATCH 102/111] Fixes issue with package not being transpiled by Babel before running tests, ensures that Babel is running locally; Removes unused Babel plugins and updates .babelrc --- .babelrc | 13 ++++--------- package.json | 20 +++++++++----------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.babelrc b/.babelrc index 9191882..1b5d503 100644 --- a/.babelrc +++ b/.babelrc @@ -1,13 +1,8 @@ { "plugins": [ - "transform-decorators-legacy", - ["transform-es2015-for-of", { - "loose": true - }], - ["transform-es2015-classes", { - "loose": true - }], - "transform-class-properties" + "transform-decorators-legacy" ], - "presets": ["latest", "stage-0", "stage-1", "stage-2", "stage-3"] + presets: [ + ["es2015", {"loose": true}] + ] } \ No newline at end of file diff --git a/package.json b/package.json index 1981c45..2f2121b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,10 @@ "node": ">= 4.0.x", "npm": ">= 1.4.x" }, + "repository": { + "type": "git", + "url": "git://github.com/meteor-space/base.git" + }, "dependencies": { "underscore": "^1.8.0", "simplecheck": "git://github.com/qejk/simplecheck.git", @@ -33,17 +37,9 @@ }, "devDependencies": { "babel-core": "^6.21.0", - "babel-plugin-transform-class-properties": "6.19.0", + "babel-cli": "^6.18.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-plugin-transform-es2015-classes": "^6.18.0", - "babel-plugin-transform-es2015-for-of": "^6.18.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.18.0", - "babel-plugin-transform-runtime": "^6.15.0", - "babel-preset-latest": "^6.16.0", - "babel-preset-stage-0": "^6.16.0", - "babel-preset-stage-1": "^6.16.0", - "babel-preset-stage-2": "^6.18.0", - "babel-preset-stage-3": "^6.17.0", + "babel-preset-es2015": "^6.14.0", "chai": "^3.5.0", "sinon": "^1.17.7", "sinon-chai": "^2.8.0", @@ -52,7 +48,9 @@ }, "scripts": { - "test": "mocha --compilers js:babel-register --recursive test/**/*.js", + "compile": "./node_modules/.bin/babel --presets es2015 -d lib/ src/", + "prepublish": "npm run compile", + "test": "npm run compile && node_modules/mocha/bin/mocha --require babel-core/register --recursive test/**/*.js", "test-watch": "npm test -- --watch" } } \ No newline at end of file From bba127f8bcd57c47d06b5ff30d3ebbb58686c589 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:47:46 +0100 Subject: [PATCH 103/111] Updates CircleCI --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index f46356a..e9553f4 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: node: - version: 4.0.0 + version: 6.0.0 dependencies: override: @@ -10,4 +10,4 @@ dependencies: test: override: - - mocha --require babel-core/register --recursive test/**/*.js + - npm test From a2cc1e55bfb635228407bbaa5086f9efb3ac35a8 Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:52:39 +0100 Subject: [PATCH 104/111] Removes preset from Babels compilation --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f2121b..f91297d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ }, "scripts": { - "compile": "./node_modules/.bin/babel --presets es2015 -d lib/ src/", + "compile": "./node_modules/.bin/babel -d lib/ src/", "prepublish": "npm run compile", "test": "npm run compile && node_modules/mocha/bin/mocha --require babel-core/register --recursive test/**/*.js", "test-watch": "npm test -- --watch" From 256515f63abcd80d053a934ac5f931656225880e Mon Sep 17 00:00:00 2001 From: qejk Date: Fri, 13 Jan 2017 13:53:34 +0100 Subject: [PATCH 105/111] Changes file paths on tests to use transpiled version of lib --- test/integration/application-with-modules.spec.js | 4 ++-- test/integration/lifecycle-hooks.tests.js | 4 ++-- test/integration/module.regressions.js | 8 ++++---- test/integration/requiring-modules.tests.js | 4 ++-- test/unit/application.unit.js | 6 +++--- test/unit/error.unit.js | 2 +- test/unit/helpers.unit.js | 2 +- test/unit/injector-annotations.unit.js | 2 +- test/unit/injector.unit.js | 6 +++--- test/unit/logger.unit.js | 6 +++--- test/unit/module.unit.js | 6 +++--- test/unit/object.unit.js | 4 ++-- test/unit/struct.unit.js | 4 ++-- 13 files changed, 29 insertions(+), 29 deletions(-) diff --git a/test/integration/application-with-modules.spec.js b/test/integration/application-with-modules.spec.js index 1e8b3bd..c0be2ea 100644 --- a/test/integration/application-with-modules.spec.js +++ b/test/integration/application-with-modules.spec.js @@ -1,5 +1,5 @@ -import Module from '../../source/module.js'; -import Application from '../../source/application.js'; +import Module from '../../lib/module.js'; +import Application from '../../lib/application.js'; describe('Building applications based on modules', function() { diff --git a/test/integration/lifecycle-hooks.tests.js b/test/integration/lifecycle-hooks.tests.js index 7e24683..d8c4aac 100644 --- a/test/integration/lifecycle-hooks.tests.js +++ b/test/integration/lifecycle-hooks.tests.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -import Module from '../../source/module.js'; -import Application from '../../source/application.js'; +import Module from '../../lib/module.js'; +import Application from '../../lib/application.js'; describe("Space.base - Application lifecycle hooks", function() { diff --git a/test/integration/module.regressions.js b/test/integration/module.regressions.js index 2f83b07..ba77711 100644 --- a/test/integration/module.regressions.js +++ b/test/integration/module.regressions.js @@ -1,7 +1,7 @@ -import Module from '../../source/module.js'; -import SpaceObject from '../../source/object.js'; -import {Injector} from '../../source/injector.js'; -import Space from '../../source/space.js'; +import Module from '../../lib/module.js'; +import SpaceObject from '../../lib/object.js'; +import {Injector} from '../../lib/injector.js'; +import Space from '../../lib/space.js'; describe("Module - regressions", function() { diff --git a/test/integration/requiring-modules.tests.js b/test/integration/requiring-modules.tests.js index 66b8cf7..9a9f221 100644 --- a/test/integration/requiring-modules.tests.js +++ b/test/integration/requiring-modules.tests.js @@ -1,5 +1,5 @@ -import Module from '../../source/module.js'; -import Application from '../../source/application.js'; +import Module from '../../lib/module.js'; +import Application from '../../lib/application.js'; describe("Space.base - Requiring modules in other modules and apps", function() { diff --git a/test/unit/application.unit.js b/test/unit/application.unit.js index 348b64e..5ee3e1b 100644 --- a/test/unit/application.unit.js +++ b/test/unit/application.unit.js @@ -1,6 +1,6 @@ -import Application from '../../source/application.js'; -import Module from '../../source/module.js'; -import {Injector} from '../../source/injector.js'; +import Application from '../../lib/application.js'; +import Module from '../../lib/module.js'; +import {Injector} from '../../lib/injector.js'; describe('Application', function() { diff --git a/test/unit/error.unit.js b/test/unit/error.unit.js index 434a2d2..b72b157 100644 --- a/test/unit/error.unit.js +++ b/test/unit/error.unit.js @@ -1,4 +1,4 @@ -import SpaceError from '../../source/error.js'; +import SpaceError from '../../lib/error.js'; describe("SpaceError", function() { diff --git a/test/unit/helpers.unit.js b/test/unit/helpers.unit.js index 12502ad..075ec81 100644 --- a/test/unit/helpers.unit.js +++ b/test/unit/helpers.unit.js @@ -1,5 +1,5 @@ import {isNil} from 'lodash'; -import Space from '../../source/space.js'; +import Space from '../../lib/space.js'; // Not available on browsers if (isNil(global)) { diff --git a/test/unit/injector-annotations.unit.js b/test/unit/injector-annotations.unit.js index 5304e28..3b175f4 100644 --- a/test/unit/injector-annotations.unit.js +++ b/test/unit/injector-annotations.unit.js @@ -1,4 +1,4 @@ -import Space from '../../source/space.js'; +import Space from '../../lib/space.js'; describe('Injector annotations', function() { diff --git a/test/unit/injector.unit.js b/test/unit/injector.unit.js index 52e1cba..8e6db3d 100644 --- a/test/unit/injector.unit.js +++ b/test/unit/injector.unit.js @@ -1,6 +1,6 @@ -import {Injector} from '../../source/injector.js'; -import SpaceObject from '../../source/object.js'; -import Space from '../../source/space.js'; +import {Injector} from '../../lib/injector.js'; +import SpaceObject from '../../lib/object.js'; +import Space from '../../lib/space.js'; describe('Injector', function() { diff --git a/test/unit/logger.unit.js b/test/unit/logger.unit.js index 94d6c0d..5ea6039 100644 --- a/test/unit/logger.unit.js +++ b/test/unit/logger.unit.js @@ -1,6 +1,6 @@ -import Logger from '../../source/logger.js'; -import LoggingAdapter from '../../source/loggers/adapter.js'; -import SpaceObject from '../../source/object.js'; +import Logger from '../../lib/logger.js'; +import LoggingAdapter from '../../lib/loggers/adapter.js'; +import SpaceObject from '../../lib/object.js'; const TestAdapter = LoggingAdapter.extend('TestAdapter', { Constructor(lib) { diff --git a/test/unit/module.unit.js b/test/unit/module.unit.js index 3c27b27..abf725b 100644 --- a/test/unit/module.unit.js +++ b/test/unit/module.unit.js @@ -1,6 +1,6 @@ -import SpaceObject from '../../source/object.js'; -import Module from '../../source/module.js'; -import {Injector} from '../../source/injector.js'; +import SpaceObject from '../../lib/object.js'; +import Module from '../../lib/module.js'; +import {Injector} from '../../lib/injector.js'; describe('Module', function() { diff --git a/test/unit/object.unit.js b/test/unit/object.unit.js index f610249..215fb1b 100644 --- a/test/unit/object.unit.js +++ b/test/unit/object.unit.js @@ -1,5 +1,5 @@ -import SpaceObject from '../../source/object.js'; -import Space from '../../source/space.js'; +import SpaceObject from '../../lib/object.js'; +import Space from '../../lib/space.js'; describe('SpaceObject', function() { diff --git a/test/unit/struct.unit.js b/test/unit/struct.unit.js index 0f48836..83703bd 100644 --- a/test/unit/struct.unit.js +++ b/test/unit/struct.unit.js @@ -1,6 +1,6 @@ import {MatchError, Integer} from 'simplecheck'; -import Struct from '../../source/struct.js'; -import SpaceObject from '../../source/object.js'; +import Struct from '../../lib/struct.js'; +import SpaceObject from '../../lib/object.js'; describe('Struct', function() { From 4b369fe9c8c57b631b21a87642b21c32d5dbf463 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 00:50:10 +0100 Subject: [PATCH 106/111] Fixes issue with SpaceError not passing arguments to construct callbacks --- src/error.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/error.js b/src/error.js index 0772859..476d8c3 100644 --- a/src/error.js +++ b/src/error.js @@ -8,7 +8,6 @@ const IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; const SpaceError = function(params) { - this._invokeConstructionCallbacks.apply(this, arguments); let data = null; if (_.isString(params)) { data = { message: params }; @@ -20,12 +19,11 @@ const SpaceError = function(params) { const properties = this.extractErrorProperties(data); this._checkFields(properties); - this._invokeConstructionCallbacks.apply(this, data); + this._invokeConstructionCallbacks.apply(this, arguments); // Copy properties to instance by default for (let [key, value] of ObjectEntries(properties)) { this[key] = value; } - return this; }; @@ -61,5 +59,8 @@ _.extend( _.extend(SpaceError, _.omit(SpaceObject, 'toString'), { __keepToStringMethod__: true // Do not override #toString method }); +SpaceError.prototype.toString = function() { + return this.message; +}; export default SpaceError; From 532859f9197b00ccb74d036a02cf68fd81780084 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 00:50:38 +0100 Subject: [PATCH 107/111] Changes temporarily Module publication --- src/module.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/module.js b/src/module.js index 326d6c9..e5338d4 100644 --- a/src/module.js +++ b/src/module.js @@ -262,7 +262,10 @@ const Module = SpaceObject.extend('Space.Module', { // Publishes a module into the space environment to make it // visible and requireable for other modules and the application publish(module, identifier) { - module.publishedAs = module.name = identifier; + // TODO: its overriding name necessary? + // TypeError: Cannot assign to read only property 'name' of function 'function GrandchildModule() { initialize.apply(this, arguments); }' + // module.publishedAs = module.name = identifier; + module.publishedAs = identifier; if (!isNil(Module.published[identifier])) { throw new Error(`Two modules tried to be published as <${identifier}>`); } else { From c8cae37b18d44b25916219aed4b40a6205195525 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 01:00:59 +0100 Subject: [PATCH 108/111] Adds build script to npm --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f91297d..3abb417 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ }, "scripts": { + "build": "npm run compile", "compile": "./node_modules/.bin/babel -d lib/ src/", "prepublish": "npm run compile", "test": "npm run compile && node_modules/mocha/bin/mocha --require babel-core/register --recursive test/**/*.js", From ed0987b9a15e66395a1978e1f9ce9d0e0655fb11 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 01:03:37 +0100 Subject: [PATCH 109/111] Updates CircleCI to not cache node_modules --- circle.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/circle.yml b/circle.yml index e9553f4..898cacc 100644 --- a/circle.yml +++ b/circle.yml @@ -5,8 +5,6 @@ machine: dependencies: override: - npm install - cache_directories: - - node_modules test: override: From 1315eb2a417cda65f7ccb410a5145fbb1da69372 Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 01:06:58 +0100 Subject: [PATCH 110/111] Fixes reference to pinned space-testing package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3abb417..13fb0ce 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "sinon": "^1.17.7", "sinon-chai": "^2.8.0", "mocha": "^3.2.0", - "space-testing": "https://github.com/meteor-space/testing.git#feature/convert-to-npm" + "space-testing": "git://github.com/meteor-space/testing.git#feature\/convert-to-npm" }, "scripts": { From 34a0351fe2fe7cbd7da0cb0de71f5773739c619a Mon Sep 17 00:00:00 2001 From: qejk Date: Sat, 14 Jan 2017 01:14:22 +0100 Subject: [PATCH 111/111] Changes construction super testing approach on Struct unit test --- test/unit/struct.unit.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/unit/struct.unit.js b/test/unit/struct.unit.js index 83703bd..3ff2c16 100644 --- a/test/unit/struct.unit.js +++ b/test/unit/struct.unit.js @@ -4,11 +4,16 @@ import SpaceObject from '../../lib/object.js'; describe('Struct', function() { + const MyMixin = { + onConstruction: sinon.spy() + }; + class MyTestStruct extends Struct { fields() { return {name: String, age: Integer}; } } + MyTestStruct.mixin([MyMixin]); class MyExtendedTestStruct extends MyTestStruct { fields() { @@ -22,13 +27,11 @@ describe('Struct', function() { expect(Struct.prototype).to.be.instanceof(SpaceObject); }); - xit("calls the super constructor", () => { - const constructorSpy = sinon.spy(SpaceObject.prototype, 'constructor'); - const data = {}; - const struct = new Struct(data); - expect(constructorSpy).to.have.been.calledWithExactly(data); - expect(constructorSpy).to.have.been.calledOn(struct); - constructorSpy.restore(); + it("calls the super constructor", () => { + const data = {name: 'Dominik', age: 26}; + const struct = new MyTestStruct(data); + expect(MyMixin.onConstruction).to.have.been.calledWithExactly(data); + expect(MyMixin.onConstruction).to.have.been.calledOn(struct); }); describe('defining fields', () => {