Skip to content

Commit

Permalink
Merge pull request #82 from ampatspell/less-experimental-models
Browse files Browse the repository at this point in the history
Models computed properties 2nd iteration
  • Loading branch information
ampatspell authored Aug 10, 2018
2 parents fdc849e + 887c657 commit 3662daa
Show file tree
Hide file tree
Showing 63 changed files with 2,429 additions and 125 deletions.
6 changes: 3 additions & 3 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ models('query.content').owner('product.type').object('data.type').named((doc, ow
## Model

``` javascript
model().owner('doc', 'product.type').object('data.type').inline({
model().owner('doc', 'product.type').inline({
prepare(owner) {
}
});
```

``` javascript
model().owner('doc', 'product.type').object('data.type').inline({
model().owner('doc', 'product.type').inline({
prepare({ doc, product }) {
this.setProperties({ doc, product });
}
Expand All @@ -145,7 +145,7 @@ model().owner('doc').named('book');
```

``` javascript
model().owner('doc', 'product.type').object('data.type').named(({ doc, product }) => {
model().owner('doc', 'product.type').named(({ doc, product }) => {
let type = doc.get('data.type');
return `products/${product.type}/components/${type}`;
}).mapping(owner => {
Expand Down
8 changes: 4 additions & 4 deletions addon/-private/experimental/model/factory.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { typeOf } from '@ember/utils';
import { assert } from '@ember/debug';
import { generateModelClass, modelFactoryForShortName } from '../../util/model';
import { modelClassForName, createModelClassFromProperties } from '../../less-experimental/util/model-factory';

const resolveObject = (parent, key, arg) => {
let factory = generateModelClass(parent, key, arg);
let factory = createModelClassFromProperties(parent, key, arg);
return {
factory,
requiresMapping: false
};
}

const resolveString = (parent, arg) => {
let factory = modelFactoryForShortName(parent, arg);
let factory = modelClassForName(parent, arg);

return {
factory,
Expand All @@ -24,7 +24,7 @@ const resolveFunction = (parent, arg) => {
let factory;

if(name) {
factory = modelFactoryForShortName(parent, name);
factory = modelClassForName(parent, name);
}

return {
Expand Down
8 changes: 4 additions & 4 deletions addon/-private/experimental/models/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { typeOf } from '@ember/utils';
import Internal from '../../internal/internal';
import { instances, destroyInstances } from './computed/instances';
import models from './computed/models';
import { generateModelClass, modelFactoryForShortName } from '../../util/model';
import { modelClassForName, createModelClassFromProperties } from '../../less-experimental/util/model-factory';
import { assert } from '@ember/debug';

export default Internal.extend({
Expand All @@ -17,11 +17,11 @@ export default Internal.extend({
let type = typeOf(factory);
const result = (type, prop) => ({ type, prop });
if(type === 'object') {
return result('class', generateModelClass(owner, key, factory));
return result('class', createModelClassFromProperties(owner, key, factory));
} else if(type === 'string') {
return result('class', modelFactoryForShortName(owner, factory));
return result('class', modelClassForName(owner, factory));
} else if(type === 'function') {
let fn = (...args) => modelFactoryForShortName(owner, factory(...args));
let fn = (...args) => modelClassForName(owner, factory(...args));
return result('function', fn);
}
assert(`models last argument must be object, string or function`, false);
Expand Down
27 changes: 27 additions & 0 deletions addon/-private/less-experimental/model/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { A } from '@ember/array';
import { assign } from '@ember/polyfills';
import property from './property';

const compact = array => A(array).compact();

const build = (opts, nested={}) => {
opts = assign({}, opts, nested);
let prop = property(opts);
prop.owner = (...args) => build(opts, { parent: compact(args) });
prop.inline = arg => build(opts, { inline: arg });
prop.named = arg => build(opts, { named: arg });
prop.mapping = arg => build(opts, { mapping: arg });
return prop;
}

export default () => {
let opts = {
parent: [],
object: [],
inline: undefined,
named: undefined,
mapping: undefined
};

return build(opts, {});
}
40 changes: 40 additions & 0 deletions addon/-private/less-experimental/model/internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Internal from '../../internal/internal';
import Runtime from './runtime';

export default Internal.extend({

parent: null,
key: null,
opts: null,

runtime(create) {
let runtime = this._runtime;
if(!runtime && create) {
let { parent, key, opts } = this.getProperties('parent', 'key', 'opts');
runtime = new Runtime(parent, key, opts, {
updated: () => this.notifyPropertyChange()
});
this._runtime = runtime;
}
return runtime;
},

notifyPropertyChange() {
let { parent, key } = this;
if(parent.isDestroying) {
return;
}
parent.notifyPropertyChange(key);
},

model(create) {
let runtime = this.runtime(create);
return runtime && runtime.content;
},

willDestroy() {
this._super(...arguments);
this._runtime && this._runtime.destroy();
}

});
19 changes: 19 additions & 0 deletions addon/-private/less-experimental/model/property.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import destroyable from '../../util/destroyable';
import { getOwner } from '@ember/application';

const get = internal => internal.model(true);
const reusable = () => false;

const create = opts => function(key) {
let parent = this;
let owner = getOwner(this);
return owner.factoryFor('zuglet:less-experimental/model/internal').create({ parent, key, opts });
}

export default opts => {
return destroyable({
create: create(opts),
reusable,
get
});
}
73 changes: 73 additions & 0 deletions addon/-private/less-experimental/model/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import ModelFactory from '../util/model-factory';
import ObjectObserver from '../util/object-observer';
import { assert } from '@ember/debug';
import { typeOf } from '@ember/utils';

const validate = (opts, delegate) => {
assert(`opts must be object`, typeOf(opts) === 'object');
assert(`delegate must be object`, typeOf(delegate) === 'object');
assert(`delegate.updated must be function`, typeOf(delegate.updated) === 'function');
}

export default class ModelsRuntime {

constructor(parent, key, opts, delegate) {
validate(opts, delegate);
this.parent = parent;
this.key = key;
this.opts = opts;
this.delegate = delegate;

this.modelFactory = new ModelFactory({
parent,
key,
inline: opts.inline,
named: opts.named,
mapping: opts.mapping,
delegate: {
mapping: () => [ parent ],
named: () => [ parent ]
}
});

this.parentObserver = new ObjectObserver({
object: parent,
observe: opts.parent,
delegate: {
updated: (object, key) => this.onParentPropertyUpdated(object, key)
}
});

this.rebuildModel(false);
}

createModel() {
return this.modelFactory.create();
}

rebuildModel(notify) {
let previous = this.content;

let content = this.createModel();
this.content = content;

if(previous) {
previous.destroy();
}

if(previous !== content && notify) {
this.delegate.updated(content);
}
}

onParentPropertyUpdated() {
this.rebuildModel(true);
}

destroy() {
this.parentObserver.destroy();
let content = this.content;
content && content.destroy();
}

}
46 changes: 46 additions & 0 deletions addon/-private/less-experimental/models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { A } from '@ember/array';
import { assign } from '@ember/polyfills';
import property from './property';

const compact = array => A(array).compact();

// foo('docs')
// foo('type', owner => owner.type)
const source = args => {
args = compact(args);
if(args.length === 1) {
return { dependencies: args, key: args[0] };
} else if(args.length > 1) {
let last = args[args.length - 1];
if(typeof last === 'function') {
let key = args.pop();
return { dependencies: args, key };
}
}
return undefined;
}

const build = (opts, nested={}) => {
opts = assign({}, opts, nested);
let prop = property(opts);
prop.owner = (...args) => build(opts, { parent: compact(args) });
prop.object = (...args) => build(opts, { object: compact(args) });
prop.inline = arg => build(opts, { inline: arg });
prop.named = arg => build(opts, { named: arg });
prop.mapping = arg => build(opts, { mapping: arg });
prop.source = (...args) => build(opts, { source: source(args) });
return prop;
}

export default (...args) => {
let opts = {
source: undefined,
parent: [],
object: [],
inline: undefined,
named: undefined,
mapping: undefined
};

return build(opts, {}).source(...args);
}
31 changes: 31 additions & 0 deletions addon/-private/less-experimental/models/internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Internal from '../../internal/internal';
import { getOwner } from '@ember/application';
import Runtime from './runtime';

export default Internal.extend({

parent: null,
key: null,
opts: null,

runtime(create) {
let runtime = this._runtime;
if(!runtime && create) {
let { parent, key, opts } = this.getProperties('parent', 'key', 'opts');
runtime = new Runtime(parent, key, opts);
this._runtime = runtime;
}
return runtime;
},

createModel() {
let content = this.runtime(true).content;
return getOwner(this.parent).factoryFor('zuglet:less-experimental/models').create({ _internal: this, content });
},

willDestroy() {
this._super(...arguments);
this._runtime && this._runtime.destroy();
}

});
4 changes: 4 additions & 0 deletions addon/-private/less-experimental/models/models.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ArrayProxy from '@ember/array/proxy';
import ModelMixin from '../../internal/model-mixin';

export default ArrayProxy.extend(ModelMixin);
19 changes: 19 additions & 0 deletions addon/-private/less-experimental/models/property.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import destroyable from '../../util/destroyable';
import { getOwner } from '@ember/application';

const get = internal => internal.model(true);
const reusable = () => false;

const create = opts => function(key) {
let parent = this;
let owner = getOwner(this);
return owner.factoryFor('zuglet:less-experimental/models/internal').create({ parent, key, opts });
}

export default opts => {
return destroyable({
create: create(opts),
reusable,
get
});
}
Loading

0 comments on commit 3662daa

Please sign in to comment.