Skip to content

Commit 7815b11

Browse files
committed
Initial commit
0 parents  commit 7815b11

File tree

9 files changed

+352
-0
lines changed

9 files changed

+352
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bower_components

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2016 Shawn Biddle
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

bower.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "memoization-behavior",
3+
"version": "0.0.1",
4+
"description": "Polymer behavior adding memoization helpers",
5+
"main": "memoization-behavior.html",
6+
"license": "MIT",
7+
"authors": [
8+
"Shawn Biddle <shawncplus@gmail.com>"
9+
],
10+
"keywords": [
11+
"web-components",
12+
"polymer",
13+
"caching"
14+
],
15+
"dependencies": {
16+
"polymer": "Polymer/polymer#^1.4.0"
17+
},
18+
"devDependencies": {
19+
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
20+
"iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
21+
"web-component-tester": "^4.0.0",
22+
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
23+
"paper-button": "PolymerElements/paper-button#^1.0.0",
24+
"iron-input": "PolymerElements/iron-input#^1.0.0"
25+
}
26+
}

demo/demo.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
6+
7+
<title>memoization-behavior demo</title>
8+
9+
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
10+
11+
<link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
12+
<link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
13+
<link rel="import" href="simple-el.html">
14+
15+
<style is="custom-style" include="demo-pages-shared-styles"></style>
16+
</head>
17+
<body>
18+
<div class="vertical-section-container centered">
19+
<h3>Basic memoization-behavior demo</h3>
20+
<p>Inputs update onChange, printCache to print the current memoization cache for <code>_computeFullName</code></p>
21+
<simple-el></simple-el>
22+
</div>
23+
</body>
24+
</html>

demo/simple-el.html

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<link rel="import" href="../../polymer/polymer.html">
2+
<link rel="import" href="../../iron-input/iron-input.html">
3+
<link rel="import" href="../../paper-button/paper-button.html">
4+
<link rel="import" href="../memoization-behavior.html">
5+
6+
<dom-module id="simple-el">
7+
<template>
8+
<h1>[[fullName]]</h1>
9+
<div>
10+
<input is="iron-input" value="{{first::change}}">
11+
<input is="iron-input" value="{{last::change}}">
12+
<paper-button on-tap="_clearNameCache" raised>Clear Cache</paper-button>
13+
<paper-button on-tap="_printCache" raised>Print Cache</paper-button>
14+
</div>
15+
<div>
16+
<h2>Cache</h2>
17+
<ul>
18+
<template is="dom-repeat" items="[[_cacheItems]]">
19+
<li>[[item.key]] =&gt; [[item.value]]</li>
20+
</template>
21+
</ul>
22+
</div>
23+
</template>
24+
<script>
25+
Polymer({
26+
27+
is: 'simple-el',
28+
29+
properties: {
30+
first: String,
31+
last: String,
32+
fullName: {
33+
type: String,
34+
computed: '_memoizeComputed("fullName", first, last)',
35+
method: '_computeFullName',
36+
},
37+
38+
_cacheItems: Array,
39+
},
40+
41+
behaviors: [
42+
Biddle.MemoizationBehavior
43+
],
44+
45+
_computeFullName: function (first, last) {
46+
console.log(`Cache miss: ${first}, ${last}`);
47+
return first + ' ' + last;
48+
},
49+
50+
_clearNameCache: function () {
51+
this._invalidateMemoizeCache(this._computeFullName);
52+
this._printCache();
53+
},
54+
55+
_printCache: function () {
56+
var cache = this._computeFullName.cache;
57+
var kv = [];
58+
for (var k in cache) {
59+
if (cache.hasOwnProperty(k)) {
60+
kv.push({key: k, value: cache[k] })
61+
}
62+
}
63+
64+
this._cacheItems = kv;
65+
}
66+
});
67+
</script>
68+
</dom-module>

index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
6+
7+
<title>memoization-behavior</title>
8+
9+
<script src="../webcomponentsjs/webcomponents-lite.js"></script>
10+
11+
<link rel="import" href="../iron-component-page/iron-component-page.html">
12+
</head>
13+
<body>
14+
<iron-component-page src="memoization-behavior.html"></iron-component-page>
15+
</body>
16+
</html>

memoization-behavior.html

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<!--
2+
@license
3+
Copyright (c) 2016 Shawn Biddle
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+
-->
24+
<script>
25+
window.Biddle = window.Biddle || {};
26+
/**
27+
`Biddle.MemoizationBehavior` allows you to define a computed property
28+
as memoizable (http://en.wikipedia.org/wiki/Memoization) with `_memoizeComputed`
29+
or more generally memoize any given function with `_memoize`
30+
31+
To implement a computed property as memoizable in the simplest fashion (although
32+
this is a trivial example, you would likely only want to memoize expensive functions)
33+
34+
behaviors: [
35+
Biddle.MemoizationBehavior,
36+
],
37+
38+
properties: {
39+
firstName: String,
40+
lastName: String,
41+
fullName: {
42+
type: String,
43+
computed: '_memoizeComputed("fullName", firstName, lastName)'
44+
method: '_getFullName'
45+
}
46+
},
47+
48+
_getFullName: function (first, last) {
49+
return first + ' ' + last;
50+
}
51+
52+
Given that the default hash function is `JSON.stringify` which may be inappropriate
53+
for object/array parameters you may wish to override the default hashing method
54+
for a specific property.
55+
56+
properties: {
57+
foo: Object,
58+
bar: Object,
59+
foobar: {
60+
type: String,
61+
computed: '_memoizeComputed("fullName", foo, bar)'
62+
method: '_computeFoobar',
63+
hashFn: (foo, bar) => foo.id + ':' + bar.id
64+
}
65+
},
66+
67+
To generally memoize any function you may do the following
68+
69+
var someValue = this._memoize(this.someExpensiveFunction)(arg2, arg2, arg3);
70+
71+
Cache invalidation happens manually with the `_invalidateMemoizeCache` method
72+
73+
var foo = this._memoize(this.bar)('Hello');
74+
// ...
75+
this._invalidateMemoizeCache(this.bar);
76+
77+
@demo demo/demo.html
78+
@polymerBehavior
79+
*/
80+
Biddle.MemoizationBehavior = {
81+
82+
/**
83+
* Default method to create the hash to key a value in the function's cache
84+
* @type {!Function}
85+
*/
86+
_defaultMemoizeHash: (...args) => JSON.stringify(args),
87+
88+
/**
89+
* Memoize a given function
90+
* @param {!Function} func Function to memoize
91+
* @param {Function} hashFn Method to create the hash key, defaults to `_defaultMemoizeHash`
92+
* @return {Function}
93+
*/
94+
_memoize: function (func, hashFn) {
95+
hashFn = hashFn || this._defaultMemoizeHash;
96+
return function (...args) {
97+
var key = '' + hashFn.apply(this, args);
98+
func.cache = func.cache || {};
99+
if (!(key in func.cache)) {
100+
func.cache[key] = func.apply(this, args);
101+
}
102+
return func.cache[key];
103+
};
104+
},
105+
106+
/**
107+
* Memoize a given computed function
108+
* @param {!String} property Name of property being computed (camelCase)
109+
* @param {...*} args Any args to pass to compute function as normal
110+
* @return {*}
111+
*/
112+
_memoizeComputed: function (property, ...args) {
113+
var propertyDef = this.properties[property];
114+
var hashFn = propertyDef.hashFn || this._defaultMemoizeHash;
115+
return this._memoize(this[propertyDef.method], hashFn).apply(this, args);
116+
},
117+
118+
/**
119+
* Invalidate the cache for a given method
120+
* @param {String} method
121+
*/
122+
_invalidateMemoizeCache: function (method) {
123+
method.cache = {};
124+
}
125+
};
126+
</script>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
6+
7+
<title>memoization-behavior test</title>
8+
9+
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
10+
<script src="../../web-component-tester/browser.js"></script>
11+
12+
<link rel="import" href="x-memoize.html">
13+
</head>
14+
<body>
15+
<test-fixture id="basic">
16+
<template>
17+
<x-memoize></x-memoize>
18+
</template>
19+
</test-fixture>
20+
21+
<script>
22+
suite('computed', function() {
23+
test('test computed property memoization', function () {
24+
var el = fixture('basic');
25+
assert.isUndefined(el.computeFullName.cache);
26+
el.first = 'Shawn';
27+
el.last = 'Biddle';
28+
assert.equal(el.fullName, 'Shawn Biddle');
29+
30+
var key = el.properties.fullName.hashFn(el.first, el.last);
31+
expect(el.computeFullName.cache).to.have.property(key);
32+
assert.equal(el.computeFullName.cache[key], el.fullName);
33+
});
34+
});
35+
</script>
36+
</body>
37+
</html>

test/x-memoize.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<link rel="import" href="../../polymer/polymer.html">
2+
<link rel="import" href="../../iron-input/iron-input.html">
3+
<link rel="import" href="../../paper-button/paper-button.html">
4+
<link rel="import" href="../memoization-behavior.html">
5+
6+
<dom-module id="x-memoize">
7+
<template>
8+
</template>
9+
<script>
10+
Polymer({
11+
12+
is: 'x-memoize',
13+
14+
properties: {
15+
first: String,
16+
last: String,
17+
fullName: {
18+
type: String,
19+
computed: '_memoizeComputed("fullName", first, last)',
20+
method: 'computeFullName',
21+
hashFn: (first, last) => first + ':' + last
22+
},
23+
},
24+
25+
behaviors: [
26+
Biddle.MemoizationBehavior
27+
],
28+
29+
computeFullName: function (first, last) {
30+
return first + ' ' + last;
31+
},
32+
});
33+
</script>
34+
</dom-module>
35+

0 commit comments

Comments
 (0)