Skip to content

Commit

Permalink
Merge pull request #15968 from emberjs/named-args
Browse files Browse the repository at this point in the history
Implement named args RFC
  • Loading branch information
rwjblue authored Dec 12, 2017
2 parents f432484 + 22fda11 commit f827a11
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 38 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,4 @@ env:
- TEST_SUITE=blueprints
- TEST_SUITE=travis-browsers DISABLE_JSCS=true DISABLE_JSHINT=true
- TEST_SUITE=sauce
- TEST_SUITE=allow-backtracking-rerender ALLOW_BACKTRACKING=true
- TEST_SUITE=each-package-tests BUILD_TYPE=alpha PUBLISH=true
5 changes: 5 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ for a detailed explanation.
Adds an ability to for developers to integrate their own custom component managers
into Ember Applications per [RFC](https://github.com/emberjs/rfcs/blob/custom-components/text/0000-custom-components.md).

* `ember-glimmer-named-arguments`

Add `{{@foo}}` syntax to access named arguments in component templates per
[RFC](https://github.com/emberjs/rfcs/pull/276).

* `ember-module-unification`

Introduces support for Module Unification
Expand Down
9 changes: 1 addition & 8 deletions broccoli/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ function getFeatures(isDebug) {
}
}

features['ember-glimmer-allow-backtracking-rerender'] = false;

if (process.env.ALLOW_BACKTRACKING) {
features['ember-glimmer-allow-backtracking-rerender'] = true;
features['ember-glimmer-detect-backtracking-rerender'] = false;
}

features['mandatory-setter'] = isDebug;
features['ember-glimmer-detect-backtracking-rerender'] = isDebug;

Expand All @@ -46,4 +39,4 @@ function toConst(features) {

module.exports.toConst = toConst;
module.exports.RELEASE = getFeatures(false);
module.exports.DEBUG = getFeatures(true);
module.exports.DEBUG = getFeatures(true);
2 changes: 1 addition & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"features-stripped-test": null,
"ember-libraries-isregistered": null,
"ember-improved-instrumentation": null,
"ember-glimmer-allow-backtracking-rerender": null,
"ember-glimmer-named-arguments": null,
"ember-routing-router-service": true,
"ember-engines-mount-params": true,
"ember-module-unification": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,33 @@ moduleFor('Components test: curly components', class extends RenderingTest {
assert.deepEqual(fooBarInstance.childViews, [fooBarBazInstance]);
}

['@test it renders passed named arguments']() {
['@feature(ember-glimmer-named-arguments) it renders passed named arguments']() {
this.registerComponent('foo-bar', {
template: '{{@foo}}'
});

this.render('{{foo-bar foo=model.bar}}', {
model: {
bar: 'Hola'
}
});

this.assertText('Hola');

this.runTask(() => this.rerender());

this.assertText('Hola');

this.runTask(() => this.context.set('model.bar', 'Hello'));

this.assertText('Hello');

this.runTask(() => this.context.set('model', { bar: 'Hola' }));

this.assertText('Hola');
}

['@test it reflects named arguments as properties']() {
this.registerComponent('foo-bar', {
template: '{{foo}}'
});
Expand Down Expand Up @@ -1063,6 +1089,30 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: something here');
}

['@feature(ember-glimmer-named-arguments) non-block with named argument']() {
this.registerComponent('non-block', {
template: 'In layout - someProp: {{@someProp}}'
});

this.render('{{non-block someProp=prop}}', {
prop: 'something here'
});

this.assertText('In layout - someProp: something here');

this.runTask(() => this.rerender());

this.assertText('In layout - someProp: something here');

this.runTask(() => this.context.set('prop', 'other thing there'));

this.assertText('In layout - someProp: other thing there');

this.runTask(() => this.context.set('prop', 'something here'));

this.assertText('In layout - someProp: something here');
}

['@test non-block with properties overridden in init']() {
let instance;
this.registerComponent('non-block', {
Expand Down Expand Up @@ -1171,7 +1221,7 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: wycats');
}

['@test this.attrs.foo === attrs.foo === foo']() {
['@feature(!ember-glimmer-named-arguments) this.attrs.foo === attrs.foo === foo']() {
this.registerComponent('foo-bar', {
template: strip`
Args: {{this.attrs.value}} | {{attrs.value}} | {{value}}
Expand Down Expand Up @@ -1208,6 +1258,46 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('Args: wat | wat | wat123123123');
}

['@feature(ember-glimmer-named-arguments) this.attrs.foo === attrs.foo === @foo === foo']() {
this.registerComponent('foo-bar', {
template: strip`
Args: {{this.attrs.value}} | {{attrs.value}} | {{@value}} | {{value}}
{{#each this.attrs.items as |item|}}
{{item}}
{{/each}}
{{#each attrs.items as |item|}}
{{item}}
{{/each}}
{{#each @items as |item|}}
{{item}}
{{/each}}
{{#each items as |item|}}
{{item}}
{{/each}}
`
});

this.render('{{foo-bar value=model.value items=model.items}}', {
model: {
value: 'wat',
items: [1, 2, 3]
}
});

this.assertStableRerender();

this.runTask(() => {
this.context.set('model.value', 'lul');
this.context.set('model.items', [1]);
});

this.assertText(strip`Args: lul | lul | lul | lul1111`);

this.runTask(() => this.context.set('model', { value: 'wat', items: [1, 2, 3] }));

this.assertText('Args: wat | wat | wat | wat123123123123');
}

['@test non-block with properties on self']() {
this.registerComponent('non-block', {
template: 'In layout - someProp: {{someProp}}'
Expand Down Expand Up @@ -1288,6 +1378,34 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('In layout - someProp: something here - In template');
}

['@feature(ember-glimmer-named-arguments) block with named argument']() {
this.registerComponent('with-block', {
template: 'In layout - someProp: {{@someProp}} - {{yield}}'
});

this.render(strip`
{{#with-block someProp=prop}}
In template
{{/with-block}}`, {
prop: 'something here'
}
);

this.assertText('In layout - someProp: something here - In template');

this.runTask(() => this.rerender());

this.assertText('In layout - someProp: something here - In template');

this.runTask(() => this.context.set('prop', 'something else'));

this.assertText('In layout - someProp: something else - In template');

this.runTask(() => this.context.set('prop', 'something here'));

this.assertText('In layout - someProp: something here - In template');
}

['@test static arbitrary number of positional parameters'](assert) {
this.registerComponent('sample-component', {
ComponentClass: Component.extend().reopenClass({
Expand Down Expand Up @@ -2966,6 +3084,21 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('MyVar1: 1 1 MyVar2: 2 2');
}

['@feature(ember-glimmer-named-arguments) using named arguments for positional params'](assert) {
let MyComponent = Component.extend();

this.registerComponent('foo-bar', {
ComponentClass: MyComponent.reopenClass({
positionalParams: ['myVar']
}),
template: 'MyVar1: {{@myVar}} {{myVar}} MyVar2: {{myVar2}} {{@myVar2}}'
});

this.render('{{foo-bar 1 myVar2=2}}');

this.assertText('MyVar1: 1 1 MyVar2: 2 2');
}

['@test can use `{{this}}` to emit the component\'s toString value [GH#14581]'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({
Expand Down Expand Up @@ -3035,7 +3168,7 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertText('Hi!');
}

['@test can access properties off of rest style positionalParams array'](assert) {
['@feature(!ember-glimmer-named-arguments) can access properties off of rest style positionalParams array'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend().reopenClass({ positionalParams: 'things' }),
// using `attrs` here to simulate `@things.length`
Expand All @@ -3046,4 +3179,15 @@ moduleFor('Components test: curly components', class extends RenderingTest {

this.assertText('3');
}

['@feature(ember-glimmer-named-arguments) can access properties off of rest style positionalParams array'](assert) {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend().reopenClass({ positionalParams: 'things' }),
template: `{{@things.length}}`
});

this.render('{{foo-bar "foo" "bar" "baz"}}');

this.assertText('3');
}
});
1 change: 0 additions & 1 deletion packages/ember-metal/lib/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
let runInTransaction, didRender, assertNotRendered;

// detect-backtracking-rerender by default is debug build only
// detect-glimmer-allow-backtracking-rerender can be enabled in custom builds
if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER) {

// there are 2 states
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import { assert } from 'ember-debug';
import { EMBER_GLIMMER_NAMED_ARGUMENTS } from 'ember/features';
import calculateLocationDisplay from '../system/calculate-location-display';

const RESERVED = ['@arguments', '@args'];

let isReserved, assertMessage;

export default function assertReservedNamedArguments(env) {
let { moduleName } = env.meta;

return {
name: 'assert-reserved-named-arguments',

visitors: {
PathExpression(node) {
if (node.original[0] === '@') {
assert(assertMessage(moduleName, node));
PathExpression({ original, loc }) {
if (isReserved(original)) {
assert(`${assertMessage(original)} ${calculateLocationDisplay(moduleName, loc)}`);
}
}
}
};
}

function assertMessage(moduleName, node) {
let path = node.original;
let source = calculateLocationDisplay(moduleName, node.loc);

return `'${path}' is not a valid path. ${source}`;
if (EMBER_GLIMMER_NAMED_ARGUMENTS) {
isReserved = name => RESERVED.indexOf(name) !== -1 || name.match(/^@[^a-z]/);
assertMessage = name => `'${name}' is reserved.`;
} else {
isReserved = name => name[0] === '@';
assertMessage = name => `'${name}' is not a valid path.`;
}
Loading

0 comments on commit f827a11

Please sign in to comment.