diff --git a/packages/ember-htmlbars/lib/keywords/unbound.js b/packages/ember-htmlbars/lib/keywords/unbound.js index e312b4f026d..af8eae44223 100644 --- a/packages/ember-htmlbars/lib/keywords/unbound.js +++ b/packages/ember-htmlbars/lib/keywords/unbound.js @@ -1,6 +1,20 @@ /** -@module ember -@submodule ember-htmlbars + The `{{unbound}}` helper can be used with bound helper invocations to + render them in their unbound form. + + ```handlebars + {{unbound (capitalize name)}} + ``` + + In the aforementioned example, if the `name` property changes, the helper + will not re-render. + + @module ember + @submodule ember-templates + + @method unbound + @for Ember.Templates.helpers + @public */ export default function unbound(morph, env, scope, originalParams, hash, template, inverse) { diff --git a/packages/ember-htmlbars/tests/helpers/collection_test.js b/packages/ember-htmlbars/tests/helpers/collection_test.js index 57f7a00b689..e0ca071614f 100644 --- a/packages/ember-htmlbars/tests/helpers/collection_test.js +++ b/packages/ember-htmlbars/tests/helpers/collection_test.js @@ -199,6 +199,7 @@ QUnit.test("empty views should be removed when content is added to the collectio }); view = EmberView.create({ + _viewRegistry: {}, listView: ListView, listController: listController, template: compile('{{#collection view.listView content=view.listController tagName="table"}} {{view.content.title}} {{/collection}}') diff --git a/packages/ember-htmlbars/tests/helpers/with_test.js b/packages/ember-htmlbars/tests/helpers/with_test.js index 41797904f1f..827e07b6c5e 100644 --- a/packages/ember-htmlbars/tests/helpers/with_test.js +++ b/packages/ember-htmlbars/tests/helpers/with_test.js @@ -249,6 +249,8 @@ QUnit.test("it should wrap context with object controller [DEPRECATED]", functio name: 'Bob Loblaw' }); + expectDeprecation(/Using the {{with}} helper with a `controller` specified/); + view = EmberView.create({ container: container, template: compile('{{#with view.person controller="person"}}{{controllerName}}{{/with}}'), @@ -379,6 +381,8 @@ QUnit.test("it should wrap keyword with object controller [DEPRECATED]", functio }); QUnit.test("destroys the controller generated with {{with foo controller='blah'}} [DEPRECATED]", function() { + expectDeprecation(/Using the {{with}} helper with a `controller` specified/); + var destroyed = false; var Controller = EmberController.extend({ willDestroy() { @@ -433,10 +437,15 @@ QUnit.test("destroys the controller generated with {{with foo as bar controller= name: 'Bob Loblaw' }); + var template; + expectDeprecation(function() { + template = compile('{{#with person controller="person" as |steve|}}{{controllerName}}{{/with}}'); + }, `Using the {{with}} helper with a \`controller\` specified (L1:C0) is deprecated and will be removed in 2.0.0.`); + view = EmberView.create({ - container: container, - template: compile('{{#with person controller="person" as |steve|}}{{controllerName}}{{/with}}'), - controller: parentController + controller: parentController, + template, + container }); registry.register('controller:person', Controller); diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index 3f0a348d945..34383b5d2bc 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -1,3 +1,4 @@ +import Ember from 'ember-metal/core'; import { set } from "ember-metal/property_set"; import { meta, @@ -251,6 +252,13 @@ ComputedPropertyPrototype.property = function() { var args; var addArg = function(property) { + Ember.deprecate( + `Depending on arrays using a dependent key ending with \`@each\` is deprecated. ` + + `Please refactor from \`Ember.computed('${property}', function() {});\` to \`Ember.computed('${property.slice(0, -6)}.[]', function() {})\`.`, + property.slice(-5) !== '@each', + { id: 'ember-metal.@each-dependent-key-leaf', until: '2.0.0' } + ); + args.push(property); }; diff --git a/packages/ember-metal/lib/core.js b/packages/ember-metal/lib/core.js index 363c160f660..767f1c19c95 100644 --- a/packages/ember-metal/lib/core.js +++ b/packages/ember-metal/lib/core.js @@ -208,7 +208,6 @@ Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; An empty function useful for some operations. Always returns `this`. @method K - @private @return {Object} @public */ diff --git a/packages/ember-metal/lib/expand_properties.js b/packages/ember-metal/lib/expand_properties.js index 28045b053ef..832d85daa4f 100644 --- a/packages/ember-metal/lib/expand_properties.js +++ b/packages/ember-metal/lib/expand_properties.js @@ -28,7 +28,8 @@ var SPLIT_REGEX = /\{|\}/; Ember.expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz' ``` - @method + @method expandProperties + @for Ember @private @param {String} pattern The property pattern to expand. @param {Function} callback The callback to invoke. It is invoked once per diff --git a/packages/ember-metal/lib/merge.js b/packages/ember-metal/lib/merge.js index eaf63297ab8..194b93b85d4 100644 --- a/packages/ember-metal/lib/merge.js +++ b/packages/ember-metal/lib/merge.js @@ -15,7 +15,7 @@ import keys from 'ember-metal/keys'; @param {Object} original The object to merge into @param {Object} updates The object to copy properties from @return {Object} - @private + @public */ export default function merge(original, updates) { if (!updates || typeof updates !== 'object') { diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index 933d45fb17c..b5a7fee2a55 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -813,7 +813,16 @@ export function observer(...args) { var func = args.slice(-1)[0]; var paths; - var addWatchedProperty = function(path) { paths.push(path); }; + var addWatchedProperty = function(path) { + Ember.deprecate( + `Depending on arrays using a dependent key ending with \`@each\` is deprecated. ` + + `Please refactor from \`Ember.observer('${path}', function() {});\` to \`Ember.computed('${path.slice(0, -6)}.[]', function() {})\`.`, + path.slice(-5) !== '@each', + { id: 'ember-metal.@each-dependent-key-leaf', until: '2.0.0' } + ); + + paths.push(path); + }; var _paths = args.slice(0, -1); if (typeof func !== "function") { diff --git a/packages/ember-metal/tests/computed_test.js b/packages/ember-metal/tests/computed_test.js index 1885e320751..6e7e417d870 100644 --- a/packages/ember-metal/tests/computed_test.js +++ b/packages/ember-metal/tests/computed_test.js @@ -59,6 +59,16 @@ QUnit.test('defining computed property should invoke property on set', function( equal(get(obj, 'foo'), 'computed bar', 'should return new value'); }); +QUnit.test('defining a computed property with a dependent key ending with @each is deprecated', function() { + expectDeprecation(function() { + computed('blazo.@each', function() { }); + }, `Depending on arrays using a dependent key ending with \`@each\` is deprecated. Please refactor from \`Ember.computed('blazo.@each', function() {});\` to \`Ember.computed('blazo.[]', function() {})\`.`); + + expectDeprecation(function() { + computed('qux', 'zoopa.@each', function() { }); + }, `Depending on arrays using a dependent key ending with \`@each\` is deprecated. Please refactor from \`Ember.computed('zoopa.@each', function() {});\` to \`Ember.computed('zoopa.[]', function() {})\`.`); +}); + var objA, objB; QUnit.module('computed should inherit through prototype', { setup() { diff --git a/packages/ember-routing-htmlbars/tests/helpers/element_action_test.js b/packages/ember-routing-htmlbars/tests/helpers/element_action_test.js index c7bce884760..f0b0002a100 100644 --- a/packages/ember-routing-htmlbars/tests/helpers/element_action_test.js +++ b/packages/ember-routing-htmlbars/tests/helpers/element_action_test.js @@ -166,6 +166,8 @@ QUnit.test("should target the current controller inside an {{each}} loop [DEPREC QUnit.test("should target the with-controller inside an {{#with controller='person'}} [DEPRECATED]", function() { var registeredTarget; + expectDeprecation(/Using the {{with}} helper with a `controller` specified/); + ActionHelper.registerAction = function({ node }) { registeredTarget = node.state.target; }; diff --git a/packages/ember-runtime/lib/computed/reduce_computed.js b/packages/ember-runtime/lib/computed/reduce_computed.js index 8ef22b10087..fafcaac782b 100644 --- a/packages/ember-runtime/lib/computed/reduce_computed.js +++ b/packages/ember-runtime/lib/computed/reduce_computed.js @@ -203,7 +203,7 @@ DependentArraysObserver.prototype = { }, resetTransformations(dependentKey, observerContexts) { - this.trackedArraysByGuid[dependentKey] = new TrackedArray(observerContexts); + this.trackedArraysByGuid[dependentKey] = new TrackedArray(observerContexts, true); }, trackAdd(dependentKey, index, newItems) { diff --git a/packages/ember-runtime/lib/computed/reduce_computed_macros.js b/packages/ember-runtime/lib/computed/reduce_computed_macros.js index 0674667dba0..38fc7cebe60 100644 --- a/packages/ember-runtime/lib/computed/reduce_computed_macros.js +++ b/packages/ember-runtime/lib/computed/reduce_computed_macros.js @@ -291,7 +291,7 @@ export function filter(dependentKey, callback) { _suppressDeprecation: true, initialize(array, changeMeta, instanceMeta) { - instanceMeta.filteredArrayIndexes = new SubArray(); + instanceMeta.filteredArrayIndexes = new SubArray(undefined, true); }, addedItem(array, item, changeMeta, instanceMeta) { diff --git a/packages/ember-runtime/lib/mixins/enumerable.js b/packages/ember-runtime/lib/mixins/enumerable.js index 4d10636c68a..f75b7bf96d3 100644 --- a/packages/ember-runtime/lib/mixins/enumerable.js +++ b/packages/ember-runtime/lib/mixins/enumerable.js @@ -259,7 +259,7 @@ export default Mixin.create({ @param {Function} callback The callback to execute @param {Object} [target] The target object to use @return {Object} receiver - @private + @public */ forEach(callback, target) { if (typeof callback !== 'function') { @@ -643,7 +643,7 @@ export default Mixin.create({ @param {Function} callback The callback to execute @param {Object} [target] The target object to use @return {Boolean} - @private + @public */ every(callback, target) { return !this.find(function(x, idx, i) { @@ -921,7 +921,7 @@ export default Mixin.create({ @method compact @return {Array} the array without null and undefined elements. - @private + @public */ compact() { return this.filter(function(value) { @@ -942,7 +942,7 @@ export default Mixin.create({ @method without @param {Object} value @return {Ember.Enumerable} - @private + @public */ without(value) { if (!this.contains(value)) { @@ -1196,7 +1196,7 @@ export default Mixin.create({ @param {String} property name(s) to sort on @return {Array} The sorted array. @since 1.2.0 - @private + @public */ sortBy() { var sortKeys = arguments; diff --git a/packages/ember-runtime/lib/mixins/sortable.js b/packages/ember-runtime/lib/mixins/sortable.js index d0a7f439c29..65a1f125e16 100644 --- a/packages/ember-runtime/lib/mixins/sortable.js +++ b/packages/ember-runtime/lib/mixins/sortable.js @@ -166,7 +166,7 @@ export default Mixin.create(MutableEnumerable, { @property arrangedContent @private */ - arrangedContent: computed('content', 'sortProperties.@each', { + arrangedContent: computed('content', 'sortProperties.[]', { get(key) { var content = get(this, 'content'); var isSorted = get(this, 'isSorted'); diff --git a/packages/ember-runtime/lib/system/array_proxy.js b/packages/ember-runtime/lib/system/array_proxy.js index 9cf7b56cbaa..e94fcf7984e 100644 --- a/packages/ember-runtime/lib/system/array_proxy.js +++ b/packages/ember-runtime/lib/system/array_proxy.js @@ -66,7 +66,7 @@ function K() { return this; } @namespace Ember @extends Ember.Object @uses Ember.MutableArray - @private + @public */ var ArrayProxy = EmberObject.extend(MutableArray, { diff --git a/packages/ember-runtime/lib/system/subarray.js b/packages/ember-runtime/lib/system/subarray.js index d4ef6c7a86f..2b88c9489eb 100644 --- a/packages/ember-runtime/lib/system/subarray.js +++ b/packages/ember-runtime/lib/system/subarray.js @@ -1,3 +1,4 @@ +import Ember from 'ember-metal/core'; import EmberError from "ember-metal/error"; import { forEach } from "ember-metal/enumerable_utils"; @@ -20,8 +21,14 @@ export default SubArray; @namespace Ember @private */ -function SubArray(length) { - if (arguments.length < 1) { length = 0; } +function SubArray(length, _suppressDeprecation) { + Ember.deprecate( + 'Ember.SubArray will be removed in 2.0.0.', + _suppressDeprecation, + { id: 'ember-metal.sub-array', until: '2.0.0' } + ); + + if (length === undefined) { length = 0; } if (length > 0) { this._operations = [new Operation(RETAIN, length)]; diff --git a/packages/ember-runtime/lib/system/tracked_array.js b/packages/ember-runtime/lib/system/tracked_array.js index 5cca92c4118..3e4e0b495c7 100644 --- a/packages/ember-runtime/lib/system/tracked_array.js +++ b/packages/ember-runtime/lib/system/tracked_array.js @@ -1,3 +1,4 @@ +import Ember from 'ember-metal/core'; import { get } from "ember-metal/property_get"; import { forEach } from "ember-metal/enumerable_utils"; @@ -18,7 +19,13 @@ export default TrackedArray; the initial items for the starting state of retain:n. @private */ -function TrackedArray(items) { +function TrackedArray(items, _suppressDeprecation) { + Ember.deprecate( + 'Ember.TrackedArray will be removed in 2.0.0.', + _suppressDeprecation, + { id: 'ember-metal.tracked-array', until: '2.0.0' } + ); + if (arguments.length < 1) { items = []; } var length = get(items, 'length'); diff --git a/packages/ember-runtime/tests/system/subarray_test.js b/packages/ember-runtime/tests/system/subarray_test.js index 51f5de51d82..981f9f5bd97 100644 --- a/packages/ember-runtime/tests/system/subarray_test.js +++ b/packages/ember-runtime/tests/system/subarray_test.js @@ -5,6 +5,7 @@ var subarray; QUnit.module('SubArray', { setup() { + expectDeprecation('Ember.SubArray will be removed in 2.0.0.'); subarray = new SubArray(); } }); diff --git a/packages/ember-runtime/tests/system/tracked_array_test.js b/packages/ember-runtime/tests/system/tracked_array_test.js index 8adbd721680..a66324b1a50 100644 --- a/packages/ember-runtime/tests/system/tracked_array_test.js +++ b/packages/ember-runtime/tests/system/tracked_array_test.js @@ -5,7 +5,11 @@ var RETAIN = TrackedArray.RETAIN; var INSERT = TrackedArray.INSERT; var DELETE = TrackedArray.DELETE; -QUnit.module('Ember.TrackedArray'); +QUnit.module('Ember.TrackedArray', { + setup: function() { + expectDeprecation('Ember.TrackedArray will be removed in 2.0.0.'); + } +}); QUnit.test("operations for a tracked array of length n are initially retain:n", function() { trackedArray = new TrackedArray([1,2,3,4]); diff --git a/packages/ember-template-compiler/lib/main.js b/packages/ember-template-compiler/lib/main.js index e0900e02ca9..e040f8997d8 100644 --- a/packages/ember-template-compiler/lib/main.js +++ b/packages/ember-template-compiler/lib/main.js @@ -18,6 +18,7 @@ import TransformAngleBracketComponents from "ember-template-compiler/plugins/tra import TransformInputOnToOnEvent from "ember-template-compiler/plugins/transform-input-on-to-onEvent"; import DeprecateViewAndControllerPaths from "ember-template-compiler/plugins/deprecate-view-and-controller-paths"; import DeprecateViewHelper from "ember-template-compiler/plugins/deprecate-view-helper"; +import DeprecateWithController from 'ember-template-compiler/plugins/deprecate-with-controller'; // used for adding Ember.Handlebars.compile for backwards compat import "ember-template-compiler/compat"; @@ -36,6 +37,7 @@ registerPlugin('ast', TransformAngleBracketComponents); registerPlugin('ast', TransformInputOnToOnEvent); registerPlugin('ast', DeprecateViewAndControllerPaths); registerPlugin('ast', DeprecateViewHelper); +registerPlugin('ast', DeprecateWithController); export { _Ember, diff --git a/packages/ember-template-compiler/lib/plugins/deprecate-with-controller.js b/packages/ember-template-compiler/lib/plugins/deprecate-with-controller.js new file mode 100644 index 00000000000..bb01c167942 --- /dev/null +++ b/packages/ember-template-compiler/lib/plugins/deprecate-with-controller.js @@ -0,0 +1,64 @@ +import Ember from 'ember-metal/core'; +import calculateLocationDisplay from 'ember-template-compiler/system/calculate-location-display'; + +/** + @module ember + @submodule ember-template-compiler +*/ + +/** + An HTMLBars AST transformation that deprecates usage of `controller` with the `{{with}}` + helper. + + @private + @class DeprecateWithController +*/ +function DeprecateWithController(options) { + // set later within HTMLBars to the syntax package + this.syntax = null; + this.options = options || {}; +} + +/** + @private + @method transform + @param {AST} ast The AST to be transformed. +*/ +DeprecateWithController.prototype.transform = function DeprecateWithController_transform(ast) { + const pluginContext = this; + const walker = new pluginContext.syntax.Walker(); + const moduleName = pluginContext.options.moduleName; + + walker.visit(ast, function(node) { + if (pluginContext.validate(node)) { + let moduleInfo = calculateLocationDisplay(moduleName, node.loc); + + Ember.deprecate( + `Using the {{with}} helper with a \`controller\` specified ${moduleInfo}is deprecated and will be removed in 2.0.0.`, + false, + { id: 'ember-template-compiler.with-controller', until: '2.0.0' } + ); + } + }); + + return ast; +}; + +DeprecateWithController.prototype.validate = function TransformWithAsToHash_validate(node) { + return (node.type === 'BlockStatement' || node.type === 'MustacheStatement') && + node.path.original === 'with' && + hashPairForKey(node.hash, 'controller'); +}; + +function hashPairForKey(hash, key) { + for (let i = 0, l = hash.pairs.length; i < l; i++) { + let pair = hash.pairs[i]; + if (pair.key === key) { + return pair; + } + } + + return false; +} + +export default DeprecateWithController; diff --git a/packages/ember-template-compiler/tests/plugins/deprecate-with-controller-test.js b/packages/ember-template-compiler/tests/plugins/deprecate-with-controller-test.js new file mode 100644 index 00000000000..8ed20d992f7 --- /dev/null +++ b/packages/ember-template-compiler/tests/plugins/deprecate-with-controller-test.js @@ -0,0 +1,13 @@ +import { compile } from 'ember-template-compiler'; + +QUnit.module('ember-template-compiler: deprecate-with-controller'); + +QUnit.test('Using `{{with}}` with `controller` hash argument provides a deprecation', function() { + expect(1); + + expectDeprecation(function() { + compile('{{#with controller="foo"}}{{/with}}', { + moduleName: 'foo/bar/baz' + }); + }, `Using the {{with}} helper with a \`controller\` specified ('foo/bar/baz' @ L1:C0) is deprecated and will be removed in 2.0.0.`); +}); diff --git a/packages/ember-views/lib/views/select.js b/packages/ember-views/lib/views/select.js index a2e38a488d0..e68680f0332 100644 --- a/packages/ember-views/lib/views/select.js +++ b/packages/ember-views/lib/views/select.js @@ -499,7 +499,7 @@ var Select = View.extend({ }); return groupedContent; - }).property('optionGroupPath', 'content.@each'), + }).property('optionGroupPath', 'content.[]'), /** The view class for option. @@ -519,7 +519,7 @@ var Select = View.extend({ } }, - selectionDidChange: observer('selection.@each', function() { + selectionDidChange: observer('selection.[]', function() { var selection = get(this, 'selection'); if (get(this, 'multiple')) { if (!isArray(selection)) { diff --git a/packages/ember-views/tests/views/view/child_views_test.js b/packages/ember-views/tests/views/view/child_views_test.js index 23431d8d507..f05e1ee2be0 100644 --- a/packages/ember-views/tests/views/view/child_views_test.js +++ b/packages/ember-views/tests/views/view/child_views_test.js @@ -103,6 +103,10 @@ QUnit.test('should remove childViews inside {{if}} on destroy', function() { run(outerView, 'set', 'value', false); equal(outerView.get('childViews.length'), 0, 'expected no views to be leaked'); + + run(function() { + outerView.destroy(); + }); }); QUnit.test('should remove childViews inside {{each}} on destroy', function() { @@ -151,4 +155,8 @@ QUnit.test('should remove childViews inside {{each}} on destroy', function() { run(outerView, 'set', 'value', false); equal(outerView.get('childViews.length'), 0, 'expected no views to be leaked'); + + run(function() { + outerView.destroy(); + }); }); diff --git a/tests/index.html b/tests/index.html index ca718ab752c..63ea29602f0 100644 --- a/tests/index.html +++ b/tests/index.html @@ -104,7 +104,7 @@ return QUnit.test(string, callback); }; } else { - var skip = QUnit.skip; + var skip = QUnit.skip || function() { }; QUnit.skip = function(string, callback) { string = "SKIPPED: " + string; return skip(string, callback);