Last active
September 21, 2022 18:03
-
-
Save bathos/ea9e5fcac668a0fa522b to your computer and use it in GitHub Desktop.
decorators
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
| // CLASS DECORATORS //////////////////////////////////////////////////////////// | |
| /* | |
| These can be thrown on to augment the class with the static properties method | |
| and $arguments, which makes it possible to generically attach them to an | |
| angular module without the angular module "directory" file needing to know any | |
| particulars about the individual providers: | |
| import hnAuth from './services/hn-auth'; | |
| import hnPoop from './directives/hn-poop'; | |
| const module = angular.module('hn', []); | |
| const providers = [ hnAuth, hnPoop ]; | |
| for (const provider of providers) | |
| module[provider.$method](...provider.$arguments); | |
| However, this isn’t quite ideal still: one still needs to *have* a master module | |
| index file, which entails importing these members & then putting them in an | |
| array. I think there is still a better approach to be found, but I suspect this | |
| fundamental idea (of making module members entirely self-descriptive and | |
| generic) will be foundational to a more sophisticated Misha-authored build step. | |
| One should only need to specify a minimal spec of *pages* in order to define a | |
| module, which should be 1:1 with a site target, e.g. enterprise: | |
| const pages = [ each, unique, page, in, enterprise ]; | |
| const module = angular.module('hn-enterprise', getExtDependencies(pages)); | |
| for (const provider of getAllProviders(pages)) | |
| module[provider.$method](...provider.$arguments); | |
| In other words, the provider decorators might be expanded to shadow the angular | |
| DI system in a way that would be walkable. Just an idea; it may simply not be | |
| worth the trouble of working out precisely how this would be done. | |
| */ | |
| const providerTemplate = method => fn => { | |
| fn.$method = method; | |
| fn.$arguments = [ fn.name, fn ]; | |
| return fn; | |
| }; | |
| export const animation = providerTemplate('animation'); | |
| export const controller = providerTemplate('controller'); | |
| export const directive = providerTemplate('directive'); | |
| export const factory = providerTemplate('factory'); | |
| export const filter = providerTemplate('filter'); | |
| export const provider = providerTemplate('provider'); | |
| export const service = providerTemplate('service'); | |
| // SIMPLE PROVIDER FUNCTIONS /////////////////////////////////////////////////// | |
| /* | |
| These are regular functions taking two args, unlike the class decorators; but | |
| they return objects with the same interface, so that they are interchangeable. | |
| */ | |
| const valueTemplate = method => (name, value) => | |
| ({ $method: method, $arguments: [ name, value ] }); | |
| export const constant = valueTemplate('constant'); | |
| export const value = valueTemplate('value'); | |
| // POOP INJECTION ////////////////////////////////////////////////////////////// | |
| /* | |
| This is the cool bit: decorator-driven angular DI. Most libs that do this still | |
| expect you to take the injections through the constructor arguments, which seems | |
| no better than simply using pre-processing like ng-annotate. Instead, injections | |
| are automatically provisioned as properties on instantiation. There is an | |
| existing lib that does this, but in other regards it does not meet our needs | |
| very well. | |
| This can also work with providers that are not given as classes, though in that | |
| case you'd be invoking it as a regular function instead of a decorator, and the | |
| function in question will need a standard angular DI function signature (though | |
| it would not need those arguments to have the same names as the injections). | |
| export default | |
| @service | |
| @inject('$interval', 'Poop') | |
| class Butthole { | |
| poopForever() { | |
| this.$interval( | |
| () => this.emit('poop', new this.Poop({ smelly: true })), | |
| 7000 | |
| ); | |
| } | |
| } | |
| */ | |
| const inject = (...names) => fn => { | |
| const $inject = fn.$inject || []; | |
| $inject.push(...names); | |
| if (!fn.$inject) fn.$inject = $inject; | |
| if (fn.$injected) return; | |
| fn.$injected = true; | |
| if (fn.constructor != Function) { | |
| return class InjectedClass { | |
| constructor(...args) { | |
| const injections = args.slice(0, $inject.length); | |
| const realArgs = args.slice($inject.length); | |
| injections.forEach((value, i) => | |
| Object.defineProperty(fn.prototype, $inject[i], { value }) | |
| ); | |
| return new fn(realArgs); | |
| // FATALITY !!!! | |
| } | |
| static get $inject() { return fn.$inject; } | |
| static get $injected() { return true; } | |
| } | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment