Skip to content

Commit

Permalink
[added] TypeScript implementation of Scheduler.addPlan
Browse files Browse the repository at this point in the history
Summary: Closes #72

Reviewers: O2 Material Motion, markwei, featherless

Reviewed By: O2 Material Motion, markwei, featherless

Subscribers: shyndman, markwei, featherless

Tags: #material_motion

Differential Revision: http://codereview.cc/D1700
  • Loading branch information
appsforartists committed Oct 17, 2016
1 parent 864c4a5 commit da1d6f2
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 5 deletions.
61 changes: 61 additions & 0 deletions packages/runtime/src/Scheduler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/** @license
* Copyright 2016 The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import makeCompoundKeySelector from './internal/makeCompoundKeySelector';

import {
Performer,
PerformerConstructor,
PlanAndTarget,
} from './types';

/**
* The Scheduler is responsible for fulfilling Plans by delegating them to the
* correct Performer.
*/
export default class Scheduler {
_performerMapSelector = makeCompoundKeySelector('PerformerType', 'target');
_performerMap:Map<any, Performer> = new Map();

/**
* The Scheduler will ensure the given plan is immediately applied to the
* given target.
*/
addPlan({ plan, target }:PlanAndTarget = {}):void {
if (!plan) {
throw new Error(`Scheduler.addPlan requires a plan`);
}

if (!target) {
throw new Error(`Scheduler.addPlan requires a target`);
}

const PerformerType:PerformerConstructor = plan._PerformerType;

const performerMapKey = this._performerMapSelector({ PerformerType, target });
let performer:Performer;

if (this._performerMap.has(performerMapKey)) {
performer = this._performerMap.get(performerMapKey);

} else {
performer = new PerformerType({ target });
this._performerMap.set(performerMapKey, performer);
}

performer.addPlan({ plan });
}
}
10 changes: 5 additions & 5 deletions packages/runtime/src/__tests__/Scheduler-addPlan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('Scheduler.addPlan',
);

planWithSpies = {
_performerType: PerformerSpy,
_PerformerType: PerformerSpy,
}
}
);
Expand Down Expand Up @@ -89,11 +89,11 @@ describe('Scheduler.addPlan',
}
);

it(`should create a performer from plan._performerType`,
it(`should create a performer from plan._PerformerType`,
() => {
scheduler.addPlan({ plan: planWithSpies, target });

expect(planWithSpies._performerType).to.be.calledWithNew;
expect(planWithSpies._PerformerType).to.be.calledWithNew;
}
);

Expand All @@ -105,10 +105,10 @@ describe('Scheduler.addPlan',
}
);

it(`should recycle performers for the same plan._performerType and target`,
it(`should recycle performers for the same plan._PerformerType and target`,
() => {
const otherPlanSamePerformerType = {
_performerType: PerformerSpy,
_PerformerType: PerformerSpy,
};

scheduler.addPlan({ plan: planWithSpies, target });
Expand Down
18 changes: 18 additions & 0 deletions packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** @license
* Copyright 2016 The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

export * from './types';
export * from './Scheduler';
56 changes: 56 additions & 0 deletions packages/runtime/src/internal/makeCompoundKeySelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/** @license
* Copyright 2016 The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

type Dict = {
[index:string]: any,
}

/**
* A compound key selector searches the object it receives for the values at
* the given keys. It returns an object that represents the intersection of
* those values; that is, every object that has the same values for
* { key1, key2 } will receive the same result. (It's idempotent.)
*
* The compound key selector is designed to generate keys to be used in a Map
* or be the selector in an RxJS groupBy operator.
*/
export default function makeCompoundKeySelector(key1:string, key2:string):(dict:Dict) => any {
const keyMap = new Map();

return function(dict) {
const value1 = dict[key1];
const value2 = dict[key2];

if (!keyMap.has(value1))
keyMap.set(value1, new Map());

const value1Map = keyMap.get(value1);

if (value1Map.has(value2)) {
return value1Map.get(value2);

} else {
// Currently returns { key1: value1, key2: value2 }, but could just as easily be a Symbol
const result = {
[key1]: value1,
[key2]: value2,
};

value1Map.set(value2, result);
return result;
}
};
}
45 changes: 45 additions & 0 deletions packages/runtime/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/** @license
* Copyright 2016 The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

interface Plan {
_PerformerType:PerformerConstructor;
}

interface Target {

}
// TODO(https://github.com/material-motion/material-motion-experiments-js/issues/70):
// Add `declare` keyword to avoid mangling on the public API surface
interface PlanAndTarget {
plan:Plan,
target:Target,
}

interface PerformerConstructor {
new (_:{ target:Target });
}

interface Performer {
addPlan(_:{ plan:Plan });
}

export {
Performer,
PerformerConstructor,
Plan,
PlanAndTarget,
Target,
};

0 comments on commit da1d6f2

Please sign in to comment.