Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(computedFrom): support expressions
fixes #149
- Loading branch information
Showing
6 changed files
with
119 additions
and
107 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,63 @@ | ||
import {subscriberCollection} from './subscriber-collection'; | ||
import {Expression} from './ast'; | ||
import {createOverrideContext} from './scope'; | ||
import {ExpressionObserver} from './expression-observer'; | ||
|
||
const computedContext = 'ComputedPropertyObserver'; | ||
export function hasDeclaredDependencies(descriptor) { | ||
return descriptor && descriptor.get && descriptor.get.dependencies && descriptor.get.dependencies.length > 0; | ||
} | ||
|
||
@subscriberCollection() | ||
export class ComputedPropertyObserver { | ||
constructor(obj, propertyName, descriptor, observerLocator) { | ||
this.obj = obj; | ||
this.propertyName = propertyName; | ||
this.descriptor = descriptor; | ||
this.observerLocator = observerLocator; | ||
} | ||
export function declarePropertyDependencies(ctor, propertyName, dependencies) { | ||
let descriptor = Object.getOwnPropertyDescriptor(ctor.prototype, propertyName); | ||
descriptor.get.dependencies = dependencies; | ||
} | ||
|
||
getValue(){ | ||
return this.obj[this.propertyName]; | ||
export function computedFrom(...rest){ | ||
return function(target, key, descriptor){ | ||
descriptor.get.dependencies = rest; | ||
return descriptor; | ||
} | ||
} | ||
|
||
setValue(newValue){ | ||
this.obj[this.propertyName] = newValue; | ||
} | ||
export class ComputedExpression extends Expression { | ||
constructor(name, dependencies) { | ||
super(); | ||
|
||
call(context) { | ||
let newValue = this.getValue(); | ||
if (this.oldValue === newValue) | ||
return; | ||
this.callSubscribers(newValue, this.oldValue); | ||
this.oldValue = newValue; | ||
return; | ||
this.name = name; | ||
this.dependencies = dependencies; | ||
this.isAssignable = true; | ||
} | ||
|
||
subscribe(context, callable) { | ||
if (!this.hasSubscribers()) { | ||
this.oldValue = this.getValue(); | ||
|
||
let dependencies = this.descriptor.get.dependencies; | ||
this.observers = []; | ||
for (let i = 0, ii = dependencies.length; i < ii; i++) { | ||
let observer = this.observerLocator.getObserver(this.obj, dependencies[i]); | ||
// todo: consider throwing when a dependency's observer is an instance of DirtyCheckProperty. | ||
this.observers.push(observer); | ||
observer.subscribe(computedContext, this); | ||
} | ||
} | ||
evaluate(scope, lookupFunctions) { | ||
return scope.bindingContext[this.name]; | ||
} | ||
|
||
this.addSubscriber(context, callable); | ||
assign(scope, value) { | ||
scope.bindingContext[this.name] = value; | ||
} | ||
|
||
unsubscribe(context, callable) { | ||
if (this.removeSubscriber(context, callable) && !this.hasSubscribers()) { | ||
this.oldValue = undefined; | ||
accept(visitor) { | ||
throw new Error('not implemented'); | ||
} | ||
|
||
let i = this.observers.length; | ||
while(i--) { | ||
this.observers[i].unsubscribe(computedContext, this); | ||
} | ||
this.observers = null; | ||
connect(binding, scope) { | ||
let dependencies = this.dependencies; | ||
let i = dependencies.length; | ||
while (i--) { | ||
dependencies[i].connect(binding, scope); | ||
} | ||
} | ||
} | ||
|
||
export function hasDeclaredDependencies(descriptor) { | ||
return descriptor && descriptor.get && descriptor.get.dependencies && descriptor.get.dependencies.length > 0; | ||
} | ||
|
||
export function declarePropertyDependencies(ctor, propertyName, dependencies) { | ||
let descriptor = Object.getOwnPropertyDescriptor(ctor.prototype, propertyName); | ||
descriptor.get.dependencies = dependencies; | ||
} | ||
|
||
export function computedFrom(...rest){ | ||
return function(target, key, descriptor){ | ||
descriptor.get.dependencies = rest; | ||
return descriptor; | ||
export function createComputedObserver(obj, propertyName, descriptor, observerLocator) { | ||
let dependencies = descriptor.get.dependencies; | ||
if (!(dependencies instanceof ComputedExpression)) { | ||
let i = dependencies.length; | ||
while (i--) { | ||
dependencies[i] = observerLocator.parser.parse(dependencies[i]); | ||
} | ||
dependencies = descriptor.get.dependencies = new ComputedExpression(propertyName, dependencies); | ||
} | ||
|
||
let scope = { bindingContext: obj, overrideContext: createOverrideContext(obj) }; | ||
return new ExpressionObserver(scope, dependencies, observerLocator); | ||
} |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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