Last active
February 15, 2020 14:39
-
-
Save kevinswiber/6452906 to your computer and use it in GitHub Desktop.
Quick service locator in JavaScript.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var ServiceLocator = require('./service_locator'); | |
| var locator = new ServiceLocator(); | |
| var Greeter = function(message) { | |
| this.message = message; | |
| }; | |
| Greeter.prototype.greet = function() { | |
| console.log(this.message); | |
| }; | |
| var greeterFn = function(message) { console.log(message); }; | |
| var greeterObj = { | |
| greet: function(message) { | |
| console.log(message); | |
| } | |
| }; | |
| locator.register('greeter1', Greeter, ['1: Hello world!']); | |
| locator.register('greeter2', greeterFn, ['2: Hello world!']); | |
| locator.register('greeter3', greeterObj); | |
| for (var i = 0; i < 3; i++) { | |
| console.log('iteration:', i); | |
| locator.resolve('greeter1').greet(); | |
| locator.resolve('greeter2'); | |
| locator.resolve('greeter3').greet('3: Hello world!'); | |
| console.log(''); | |
| } | |
| /* | |
| Output: | |
| iteration: 0 | |
| 1: Hello world! | |
| 2: Hello world! | |
| 3: Hello world! | |
| iteration: 1 | |
| 1: Hello world! | |
| 2: Hello world! | |
| 3: Hello world! | |
| iteration: 2 | |
| 1: Hello world! | |
| 2: Hello world! | |
| 3: Hello world! | |
| */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var ServiceLocator = require('./service_locator'); | |
| var locator = new ServiceLocator(); | |
| var Greeting = function(message) { | |
| this.message = message; | |
| }; | |
| var Greeter = function(greeting) { | |
| this.greeting = greeting; | |
| }; | |
| Greeter.prototype.greet = function() { | |
| return this.greeting.message; | |
| }; | |
| var Printer = function(greeter) { | |
| this.greeter = greeter; | |
| }; | |
| Printer.prototype.print = function() { | |
| console.log(this.greeter.greet()); | |
| }; | |
| // Demo 1: Basic register/resolve behavior and component dependencies. | |
| locator.register('greeting', Greeting, ['Hello world!']); | |
| locator.register('greeter', Greeter, [locator.component('greeting')]); | |
| locator.register('printer', Printer, [locator.component('greeter')]); | |
| var printer; | |
| printer = locator.resolve('printer'); | |
| printer.print(); | |
| // Output: Hello world! | |
| /////////////////////////// | |
| // Demo 2: Transient lifestyle behavior and dynamic dependencies. | |
| var now = function() { | |
| return 'The time is: ' + new Date(); | |
| }; | |
| locator.register('greeting', Greeting, [locator.dynamic(now)], 'transient'); | |
| locator.register('greeter', Greeter, [locator.component('greeting')], 'transient'); | |
| locator.register('printer', Printer, [locator.component('greeter')], 'transient'); | |
| printer = locator.resolve('printer'); | |
| printer.print(); | |
| setTimeout(function() { | |
| printer = locator.resolve('printer'); | |
| printer.print(); | |
| }, 3000); | |
| // Sample Output: | |
| // | |
| // The time is: Thu Sep 05 2013 14:53:58 GMT-0400 (EDT) | |
| // The time is: Thu Sep 05 2013 14:54:01 GMT-0400 (EDT) | |
| // | |
| ////////////////////////// |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Implementation = function(constructor, args) { | |
| this.constructor = constructor; | |
| this.args = args; | |
| }; | |
| var Definition = function(name, constructor, args, lifestyle) { | |
| this.name = name; | |
| this.value = new Implementation(constructor, args); | |
| this.dependencies = []; | |
| this.lifestyle = lifestyle || 'singleton'; | |
| this.instance = null; | |
| }; | |
| var ServiceLocator = module.exports = function() { | |
| this.entries = {}; | |
| this.dependents = {}; | |
| }; | |
| ServiceLocator.prototype.register = function(name, constructor, args, lifestyle) { | |
| var definition = new Definition(name, constructor, args, lifestyle); | |
| args = args || []; | |
| var self = this; | |
| args.forEach(function(arg) { | |
| if (typeof arg === 'object' && arg['$type'] | |
| && arg['$type'] === 'component') { | |
| var $name = arg['$name']; | |
| definition.dependencies.push($name); | |
| if (!self.dependents[$name]) { | |
| self.dependents[$name] = []; | |
| } | |
| self.dependents[$name].push(definition.name); | |
| } | |
| }); | |
| this.evict(name); | |
| this.entries[name] = definition; | |
| }; | |
| ServiceLocator.prototype.evict = function(name) { | |
| var self = this; | |
| if (self.dependents[name]) { | |
| self.dependents[name].forEach(function(dep) { | |
| self.entries[dep].instance = null; | |
| self.evict(dep); | |
| }); | |
| } | |
| }; | |
| ServiceLocator.prototype.resolve = function(name) { | |
| var definition = this.entries[name]; | |
| if (!definition) { | |
| var message = 'No definition for `' + name + '` exists.'; | |
| throw new Error(message); | |
| } | |
| var obj; | |
| if (definition.instance) { | |
| if (typeof definition.instance === 'function') { | |
| obj = definition.instance(); | |
| } else if (typeof definition.instance === 'object') { | |
| obj = definition.instance; | |
| } | |
| } else { | |
| var value = definition.value; | |
| var constructor = value.constructor; | |
| var tempArgs = value.args || []; | |
| var args = []; | |
| var self = this; | |
| tempArgs.forEach(function(arg) { | |
| if (typeof arg === 'object' && arg['$type']) { | |
| var type = arg['$type']; | |
| if (type === 'component') { | |
| arg = self.resolve(arg['$name']); | |
| definition.dependencies.push(arg['$name']); | |
| } else if (type === 'dynamic') { | |
| arg = arg['$fn'](); | |
| } | |
| } | |
| args.push(arg); | |
| }); | |
| if (typeof constructor === 'function') { | |
| obj = Object.create(constructor.prototype); | |
| obj.constructor.apply(obj, args); | |
| if (!Object.keys(constructor.prototype).length && | |
| !Object.keys(obj).length) { | |
| // non-constructor function | |
| // bind args to function for entry instance | |
| var boundArgs = [constructor].concat(args); | |
| if (definition.lifestyle === 'singleton') { | |
| definition.instance = constructor.bind.apply(constructor, boundArgs); | |
| } | |
| } else { | |
| if (definition.lifestyle === 'singleton') { | |
| definition.instance = obj; | |
| } | |
| } | |
| } else if (typeof constructor === 'object') { | |
| obj = constructor; | |
| if (definition.lifestyle === 'singleton') { | |
| definition.instance = obj; | |
| } | |
| } | |
| } | |
| return obj; | |
| }; | |
| ServiceLocator.prototype.component = function(name) { | |
| return { | |
| $type: 'component', | |
| $name: name | |
| }; | |
| }; | |
| ServiceLocator.prototype.dynamic = function(fn) { | |
| return { | |
| $type: 'dynamic', | |
| $fn: fn | |
| }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment