diff --git a/ui-v2/app/utils/computed/factory.js b/ui-v2/app/utils/computed/factory.js new file mode 100644 index 000000000..72539972e --- /dev/null +++ b/ui-v2/app/utils/computed/factory.js @@ -0,0 +1,18 @@ +/** + * Gives you factory function to create a specified type of ComputedProperty + * Largely taken from https://github.com/emberjs/ember.js/blob/v2.18.2/packages/ember-metal/lib/computed.js#L529 + * but configurable from the outside (IoC) so its reuseable + * + * @param {Class} ComputedProperty - ComputedProperty to use for the factory + * @returns {function} - Ember-like `computed` function (see https://www.emberjs.com/api/ember/2.18/classes/ComputedProperty) + */ +export default function(ComputedProperty) { + return function() { + const args = [...arguments]; + const cp = new ComputedProperty(args.pop()); + if (args.length > 0) { + cp.property(...args); + } + return cp; + }; +} diff --git a/ui-v2/app/utils/computed/purify.js b/ui-v2/app/utils/computed/purify.js new file mode 100644 index 000000000..3c9eba341 --- /dev/null +++ b/ui-v2/app/utils/computed/purify.js @@ -0,0 +1,42 @@ +import { get } from '@ember/object'; + +/** + * Converts a conventional non-pure Ember `computed` function into a pure one + * (see https://github.com/emberjs/rfcs/blob/be351b059f08ac0fe709bc7697860d5064717a7f/text/0000-tracked-properties.md#avoiding-dependency-hell) + * + * @param {function} computed - a computed function to 'purify' (convert to a pure function) + * @param {function} filter - Optional string filter function to pre-process the names of computed properties + * @returns {function} - A pure `computed` function + */ + +export default function(computed, filter) { + return function() { + let args = [...arguments]; + let success = function(value) { + return value; + }; + // pop the user function off the end + if (typeof args[args.length - 1] === 'function') { + success = args.pop(); + } + if (typeof filter === 'function') { + args = filter(args); + } + // this is the 'conventional' `computed` + const cb = function(name) { + return success.apply( + this, + args.map(item => { + // Right now this just takes the first part of the path so: + // `items.[]` or `items.@each.prop` etc + // gives you `items` which is 'probably' what you expect + // it won't work with something like `item.objects.[]` + // it could potentially be made to do so, but we don't need that right now at least + return get(this, item.split('.')[0]); + }) + ); + }; + // concat/push the user function back on + return computed(...args.concat([cb])); + }; +}