diff --git a/features.json b/features.json index 7fe37c29cf3..603adccc7a8 100644 --- a/features.json +++ b/features.json @@ -15,7 +15,8 @@ "ember-metal-injected-properties": null, "mandatory-setter": "development-only", "ember-routing-fire-activate-deactivate-events": null, - "ember-testing-pause-test": null + "ember-testing-pause-test": null, + "improved-cp-syntax": null }, "debugStatements": [ "Ember.warn", diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index 78f77df7fee..ecfdf6798cb 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -500,7 +500,7 @@ ComputedPropertyPrototype.teardown = function(obj, keyName) { The function should accept two parameters, key and value. If value is not undefined you should set the value first. In either case return the current value of the property. - + A computed property defined in this way might look like this: ```js @@ -516,7 +516,7 @@ ComputedPropertyPrototype.teardown = function(obj, keyName) { var client = Person.create(); client.get('fullName'); // 'Betty Jones' - + client.set('lastName', 'Fuller'); client.get('fullName'); // 'Betty Fuller' ``` @@ -539,21 +539,41 @@ ComputedPropertyPrototype.teardown = function(obj, keyName) { @method computed @for Ember @param {String} [dependentKeys*] Optional dependent keys that trigger this computed property. - @param {Function} func The computed property function. + @param {Function} opts The computed property function. @return {Ember.ComputedProperty} property descriptor instance */ -function computed(func) { - var args; +function computed(opts) { + var args, func; if (arguments.length > 1) { args = a_slice.call(arguments); - func = args.pop(); + opts = args.pop(); + } + + if (Ember.FEATURES.isEnabled('improved-cp-syntax')){ + if (typeof opts === "object"){ + if (opts.set){ + func = function(keyName, value, oldValue){ + if (arguments.length > 1){ + return opts.set.call(this, keyName, value, oldValue); + } else { + return opts.get.call(this, keyName); + } + }; + } else { + func = opts.get; + } + } else if (typeof opts === "function"){ + func = opts; + Ember.deprecate("Using the same function as getter and setter is deprecated. Pass an object containing `get` and `set` properties instead", func.length < 2); + } } + func = func || opts; + if (typeof func !== "function") { throw new EmberError("Computed Property declared without a property function"); } - var cp = new ComputedProperty(func); if (args) { diff --git a/packages/ember-metal/tests/computed_test.js b/packages/ember-metal/tests/computed_test.js index a9bcd8961b4..97ec9a8ba20 100644 --- a/packages/ember-metal/tests/computed_test.js +++ b/packages/ember-metal/tests/computed_test.js @@ -591,6 +591,66 @@ testBoth('chained dependent keys should evaluate computed properties lazily', fu }); +// .......................................................... +// FEATURE 'improved-cp-syntax' +// + +if (Ember.FEATURES.isEnabled('ember-metal-injected-properties')){ + QUnit.module('computed - improved cp syntax'); + + test('setter and getters are passed using an object', function() { + var testObj = Ember.Object.extend({ + a: '1', + b: '2', + aInt: computed('a', { + get: function(keyName){ + equal(keyName, 'aInt', 'getter receives the keyName'); + return parseInt(this.get('a')); + }, + set: function(keyName, value, oldValue){ + equal(keyName, 'aInt', 'setter receives the keyName'); + equal(value, 123, 'setter receives the new value'); + equal(oldValue, 1, 'setter receives the old value'); + this.set('a', ""+value); // side effect + return parseInt(this.get('a')); + } + }) + }).create(); + + ok(testObj.get('aInt') === 1, 'getter works'); + testObj.set('aInt', 123); + ok(testObj.get('a') === '123', 'setter works'); + ok(testObj.get('aInt') === 123, 'cp has been updated too'); + }); + + test('setter can be omited', function() { + var testObj = Ember.Object.extend({ + a: '1', + b: '2', + aInt: computed('a', { + get: function(keyName){ + equal(keyName, 'aInt', 'getter receives the keyName'); + return parseInt(this.get('a')); + } + }) + }).create(); + + ok(testObj.get('aInt') === 1, 'getter works'); + ok(testObj.get('a') === '1'); + testObj.set('aInt', '123'); + ok(testObj.get('aInt') === '123', 'cp has been updated too'); + }); + + test('Passing a function that acts both as getter and setter is deprecated', function() { + var regex = /Using the same function as getter and setter is deprecated/; + expectDeprecation(function(){ + Ember.Object.extend({ + aInt: computed('a', function(keyName, value, oldValue){}) + }); + }, regex); + }); +} + // .......................................................... // BUGS //