Skip to content

Commit ed6deea

Browse files
committed
Sync with changes made on master.
1 parent d44969a commit ed6deea

2 files changed

Lines changed: 221 additions & 25 deletions

File tree

lib/legacy/legacy-data-mixin.html

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,51 @@
99
-->
1010

1111
<link rel="import" href="class.html">
12+
<link rel="import" href="../polymer.html">
1213
<link rel="import" href="../utils/mixin.html">
1314

1415
<script>
1516
(function() {
1617
'use strict';
1718

1819
const UndefinedArgumentError = class extends Error {
19-
constructor(message) {
20+
constructor(message, arg) {
2021
super(message);
22+
this.arg = arg;
2123
this.name = this.constructor.name;
2224
// Affordances for ensuring instanceof works after babel ES5 compilation
25+
// TODO(kschaaf): Remove after polymer CLI updates to newer Babel that
26+
// sets the constructor/prototype correctly for subclassed builtins
2327
this.constructor = UndefinedArgumentError;
2428
this.__proto__ = UndefinedArgumentError.prototype;
2529
}
2630
};
2731

32+
/**
33+
* Wraps effect functions to catch `UndefinedArgumentError`s and warn.
34+
*
35+
* @param {Object=} effect Effect metadata object
36+
* @param {Object=} fnName Name of user function, if known
37+
* @return {?Object} Effect metadata object
38+
*/
39+
function wrapEffect(effect, fnName) {
40+
if (effect && effect.fn) {
41+
const fn = effect.fn;
42+
effect.fn = function() {
43+
try {
44+
fn.apply(this, arguments);
45+
} catch (e) {
46+
if (e instanceof UndefinedArgumentError) {
47+
console.warn(`Argument '${e.arg}'${fnName ?` for method '${fnName}'` : ''} was undefined. Ensure it has an undefined check.`);
48+
} else {
49+
throw e;
50+
}
51+
}
52+
};
53+
}
54+
return effect;
55+
}
56+
2857
/**
2958
* Mixin to selectively add back Polymer 1.x's `undefined` rules
3059
* governing when observers & computing functions run based
@@ -38,6 +67,11 @@
3867
* 2.x+ by allowing legacy code to work while identifying observers and
3968
* computing functions that need undefined checks to work without
4069
* the mixin in Polymer 2.
70+
*
71+
* @mixinFunction
72+
* @polymer
73+
* @summary Mixin to selectively add back Polymer 1.x's `undefined` rules
74+
* governing when observers & computing functions run.
4175
*/
4276
Polymer.LegacyDataMixin = Polymer.dedupingMixin(superClass => {
4377

@@ -59,12 +93,16 @@
5993
*/
6094
_marshalArgs(args, path, props) {
6195
const vals = super._marshalArgs(args, path, props);
62-
if (this._legacyUndefinedCheck && args.length > 1) {
63-
for (let i=0; i<args.length; i++) {
96+
// Per legacy data rules, single-property observers (whether in `properties`
97+
// and in `observers`) are called regardless of whether their argument is
98+
// undefined or not. Multi-property observers must have all arguments defined
99+
if (this._legacyUndefinedCheck && vals.length > 1) {
100+
for (let i=0; i<vals.length; i++) {
64101
if (vals[i] === undefined) {
65102
// Break out of effect's control flow; will be caught in
66103
// wrapped property effect function below
67-
throw new UndefinedArgumentError(`argument '${args[i].name}' is undefined; ensure it has an undefined check`);
104+
const name = args[i].name;
105+
throw new UndefinedArgumentError(`Argument '${name}' is undefined. Ensure it has an undefined check.`, name);
68106
}
69107
}
70108
}
@@ -81,22 +119,23 @@
81119
* @return {void}
82120
* @protected
83121
*/
84-
_addPropertyEffect(property, type, effect) {
85-
if (effect && effect.fn) {
86-
const fn = effect.fn;
87-
effect.fn = function() {
88-
try {
89-
fn.apply(this, arguments);
90-
} catch (e) {
91-
if (e instanceof UndefinedArgumentError) {
92-
console.warn(e.message);
93-
} else {
94-
throw e;
95-
}
96-
}
97-
};
98-
}
99-
return super._addPropertyEffect(property, type, effect);
122+
_addPropertyEffect(property, type, effect) {
123+
return super._addPropertyEffect(property, type,
124+
wrapEffect(effect, effect && effect.info && effect.info.methodName));
125+
}
126+
127+
/**
128+
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
129+
* catch `UndefinedArgumentError`s and warn.
130+
*
131+
* @param {Object} templateInfo Template metadata to add effect to
132+
* @param {string} prop Property that should trigger the effect
133+
* @param {Object=} effect Effect metadata object
134+
* @return {void}
135+
* @protected
136+
*/
137+
static _addTemplatePropertyEffect(templateInfo, prop, effect) {
138+
return super._addTemplatePropertyEffect(templateInfo, prop, wrapEffect(effect));
100139
}
101140

102141
}
@@ -105,6 +144,9 @@
105144

106145
});
107146

147+
// LegacyDataMixin is applied to base class _before_ metaprogramming, to
148+
// ensure override of _addPropertyEffect et.al. are used by metaprogramming
149+
// performed in _finalizeClass
108150
const Class = Polymer.Class;
109151
Polymer.Class = (info, mixin) => Class(info,
110152
superClass => mixin ?

test/unit/legacy-data.html

Lines changed: 159 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,34 @@
1919
<body>
2020

2121
<dom-module id="x-data">
22+
<template>
23+
<div id="child"
24+
computed-single="[[computeSingle(inlineSingleDep)]]"
25+
computed-multi="[[computeMulti(inlineMultiDep1, inlineMultiDep2)]]">
26+
</div>
27+
</template>
2228
<script>
2329
HTMLImports.whenReady(() => {
2430
Polymer({
2531
is: 'x-data',
32+
_legacyUndefinedCheck: true,
2633
properties: {
2734
singleProp: String,
2835
multiProp1: String,
29-
multiProp2: String
36+
multiProp2: String,
37+
computedSingleDep: String,
38+
computedMultiDep1: String,
39+
computedMultiDep2: String,
40+
inlineSingleDep: String,
41+
inlineMultiDep1: String,
42+
inlineMultiDep2: String,
43+
computedSingle: {
44+
computed: 'computeSingle(computedSingleDep)'
45+
},
46+
computedMulti: {
47+
computed: 'computeMulti(computedMultiDep1, computedMultiDep2)'
48+
}
3049
},
31-
_legacyUndefinedCheck: true,
3250
observers: [
3351
'staticObserver("staticObserver")',
3452
'singlePropObserver(singleProp)',
@@ -39,6 +57,8 @@
3957
this.singlePropObserver = sinon.spy();
4058
this.multiPropObserver = sinon.spy();
4159
this.staticObserver = sinon.spy();
60+
this.computeSingle = sinon.spy((inlineSingleDep) => `[${inlineSingleDep}]`);
61+
this.computeMulti = sinon.spy((inlineMultiDep1, inlineMultiDep2) => `[${inlineMultiDep1},${inlineMultiDep2}]`);
4262
},
4363
throws() {
4464
throw new Error('real error');
@@ -72,6 +92,42 @@
7292
</template>
7393
</test-fixture>
7494

95+
<test-fixture id="declarative-single-computed">
96+
<template>
97+
<x-data computed-single-dep="a"></x-data>
98+
</template>
99+
</test-fixture>
100+
101+
<test-fixture id="declarative-multi-one-computed">
102+
<template>
103+
<x-data computed-multi-dep1="b"></x-data>
104+
</template>
105+
</test-fixture>
106+
107+
<test-fixture id="declarative-multi-all-computed">
108+
<template>
109+
<x-data computed-multi-dep1="b" computed-multi-dep2="c"></x-data>
110+
</template>
111+
</test-fixture>
112+
113+
<test-fixture id="declarative-single-computed-inline">
114+
<template>
115+
<x-data inline-single-dep="a"></x-data>
116+
</template>
117+
</test-fixture>
118+
119+
<test-fixture id="declarative-multi-one-computed-inline">
120+
<template>
121+
<x-data inline-multi-dep1="b"></x-data>
122+
</template>
123+
</test-fixture>
124+
125+
<test-fixture id="declarative-multi-all-computed-inline">
126+
<template>
127+
<x-data inline-multi-dep1="b" inline-multi-dep2="c"></x-data>
128+
</template>
129+
</test-fixture>
130+
75131
<script>
76132
(function() {
77133

@@ -83,6 +139,10 @@
83139
callCounts.singlePropObserver || 0, 'singlePropObserver call count wrong');
84140
assert.equal(el.multiPropObserver.callCount,
85141
callCounts.multiPropObserver || 0, 'multiPropObserver call count wrong');
142+
assert.equal(el.computeSingle.callCount,
143+
callCounts.computeSingle || 0, 'computeSingle call count wrong');
144+
assert.equal(el.computeMulti.callCount,
145+
callCounts.computeMulti || 0, 'computeMulti call count wrong');
86146
assert.equal(console.warn.callCount, callCounts.warn || 0,
87147
'console.warn call count wrong');
88148
}
@@ -103,9 +163,15 @@
103163
el.parentNode.removeChild(el);
104164
});
105165

106-
const singleProp = 'a';
107-
const multiProp1 = 'b';
108-
const multiProp2 = 'c';
166+
const singleProp = 'singleProp';
167+
const multiProp1 = 'multiProp1';
168+
const multiProp2 = 'multiProp2';
169+
const computedSingleDep = 'computedSingleDep';
170+
const computedMultiDep1 = 'computedMultiDep1';
171+
const computedMultiDep2 = 'computedMultiDep2';
172+
const inlineSingleDep = 'inlineSingleDep';
173+
const inlineMultiDep1 = 'inlineMultiDep1';
174+
const inlineMultiDep2 = 'inlineMultiDep2';
109175

110176
suite('check disabled', () => {
111177
test('no arguments defined', () => {
@@ -144,6 +210,36 @@
144210
el.multiProp2 = undefined;
145211
assertEffects({multiPropObserver: 3});
146212
});
213+
test('computeSingle argument defined', () => {
214+
setupElement(false, {computedSingleDep});
215+
assertEffects({computeSingle: 1});
216+
assert.equal(el.computedSingle, '[computedSingleDep]');
217+
});
218+
test('one computeMulti argument defined', () => {
219+
setupElement(false, {computedMultiDep1});
220+
assertEffects({computeMulti: 1});
221+
assert.equal(el.computedMulti, '[computedMultiDep1,undefined]');
222+
});
223+
test('all computeMulti argument defined', () => {
224+
setupElement(false, {computedMultiDep1, computedMultiDep2});
225+
assertEffects({computeMulti: 1});
226+
assert.equal(el.computedMulti, '[computedMultiDep1,computedMultiDep2]');
227+
});
228+
test('inline computeSingle argument defined', () => {
229+
setupElement(false, {inlineSingleDep});
230+
assertEffects({computeSingle: 1});
231+
assert.equal(el.$.child.computedSingle, '[inlineSingleDep]');
232+
});
233+
test('one inline computeMulti argument defined', () => {
234+
setupElement(false, {inlineMultiDep1});
235+
assertEffects({computeMulti: 1});
236+
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,undefined]');
237+
});
238+
test('all inline computeMulti argument defined', () => {
239+
setupElement(false, {inlineMultiDep1, inlineMultiDep2});
240+
assertEffects({computeMulti: 1});
241+
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
242+
});
147243
});
148244

149245
suite('warn', () => {
@@ -183,6 +279,36 @@
183279
el.multiProp2 = undefined;
184280
assertEffects({multiPropObserver: 1, warn: 2});
185281
});
282+
test('computeSingle argument defined', () => {
283+
setupElement(true, {computedSingleDep});
284+
assertEffects({computeSingle: 1});
285+
assert.equal(el.computedSingle, '[computedSingleDep]');
286+
});
287+
test('one computeMulti argument defined', () => {
288+
setupElement(true, {computedMultiDep1});
289+
assertEffects({warn: 1});
290+
assert.equal(el.computedMulti, undefined);
291+
});
292+
test('all computeMulti argument defined', () => {
293+
setupElement(true, {computedMultiDep1, computedMultiDep2});
294+
assertEffects({computeMulti: 1});
295+
assert.equal(el.computedMulti, '[computedMultiDep1,computedMultiDep2]');
296+
});
297+
test('inline computeSingle argument defined', () => {
298+
setupElement(true, {inlineSingleDep});
299+
assertEffects({computeSingle: 1});
300+
assert.equal(el.$.child.computedSingle, '[inlineSingleDep]');
301+
});
302+
test('one inline computeMulti argument defined', () => {
303+
setupElement(true, {inlineMultiDep1});
304+
assertEffects({warn: 1});
305+
assert.equal(el.$.child.computedMulti, undefined);
306+
});
307+
test('all inline computeMulti argument defined', () => {
308+
setupElement(true, {inlineMultiDep1, inlineMultiDep2});
309+
assertEffects({computeMulti: 1});
310+
assert.equal(el.$.child.computedMulti, '[inlineMultiDep1,inlineMultiDep2]');
311+
});
186312
});
187313
});
188314

@@ -209,6 +335,34 @@
209335
el = fixture('declarative-multi-all');
210336
assertEffects({multiPropObserver: 1});
211337
});
338+
test('computeSingle argument defined', () => {
339+
el = fixture('declarative-single-computed');
340+
assertEffects({computeSingle: 1});
341+
assert.equal(el.computedSingle, '[a]');
342+
});
343+
test('one computeMulti arguments defined', () => {
344+
el = fixture('declarative-multi-one-computed');
345+
assertEffects({computeMulti: 0, warn: 1});
346+
assert.equal(el.computedMulti, undefined);
347+
});
348+
test('all computeMulti defined', () => {
349+
el = fixture('declarative-multi-all-computed');
350+
assert.equal(el.computedMulti, '[b,c]');
351+
});
352+
test('inline computeSingle argument defined', () => {
353+
el = fixture('declarative-single-computed-inline');
354+
assertEffects({computeSingle: 1});
355+
assert.equal(el.$.child.computedSingle, '[a]');
356+
});
357+
test('inline one computeMulti arguments defined', () => {
358+
el = fixture('declarative-multi-one-computed-inline');
359+
assertEffects({computeMulti: 0, warn: 1});
360+
assert.equal(el.$.child.computedMulti, undefined);
361+
});
362+
test('inline all computeMulti defined', () => {
363+
el = fixture('declarative-multi-all-computed-inline');
364+
assert.equal(el.$.child.computedMulti, '[b,c]');
365+
});
212366
});
213367
});
214368

0 commit comments

Comments
 (0)