Time to get excited! I’ve just released a new library called Ember Auto for nicer computed properties. It supplies the dependent properties as arguments which means you can just use them instead of this.getting.
Most of my apps have a pattern of declaring local variables for the various properties I will need to access before any of the actual logic starts. This lib makes all of that ‘extra’ setup code go away.
I’ve been integrating this into an app I am working on, and it has made the code quite a bit easier to read.
Perhaps, after a shakedown period, this could be added to Ember core.
I only mention it because it also has some convenience methods for abstracting away some of the boilerplate around setters and getters. So for example in coffeescript you have a very succinct shorthand for computed properties with a special syntax.
@eccegordo yes I think emberscript’s an incredible project.
I think that for various reasons though not as many people will use it. Even plain coffeescript is a bit divisive.
What I wanted to do with this is make something that people can use to improve their existing codebases without considering tradeoffs.
@rwjblue I’d love for this to be added to core, after a shakedown period as you say. I don’t think there would be any barriers, just consideration about how .property() would exist alongside .auto().
Hey @gunn – this looks very intriguing. I’m a calculated property addict and a coding neat freak and this “problem” has always driven me nuts.
I noticed that you’re not supporting setting properties yet. Are there any ideas on how that might work given this new convention? (arguments.length > 1 would clearly no longer be a valid check).
I like that solution. In the spirit of cleaner/shorter syntax (and perhaps a throwaway idea)…what if it looked something like this? That might open the door for some built in patterns for setting calculated properties.
fullName : function(first, last, setter) {
setter(function(value) {
var names = value.split(/\s+/);
this.set("firstName", names[0]);
this.set("lastName", names[1]);
return value;
});
return first + " " + last;
}.auto("firstName", "lastName")
@alexspeller’s approach looks very promising if we don’t mind using an eval.
One thing we need to decide is how to deal with naming conflicts - we need a way to map the keys name, user.name, and userName to different argument names.
Here are two options:
1: Optionally provide overrides as arguments
greeting: function(name) {
return "Hi " + name + " your login is: " + userName;
}.auto("user.name", "userName")
2: Use object literals to provide pairs of argNames and keyPaths.
greeting: function() {
return "Hi " + name + " your login is: " + userName;
}.auto({name: "user.name"}, "userName")
Option 2 also lets us get closer to backwards compatible setters, although the arguments.length > 1 check would still have to change:
I personally like the idea of using object literals. It seems reasonably explicit and doesn’t take a lot of cognitive overhead to figure out what is going on. I think it is important to consider people coming to an existing code base and seeing this code, and not knowing much about ember yet. Seems like the object literals would be easiest to grok, IMHO.
I like the direction this is going. Another alternative implementation is to pass the parameters as object literals. The first parameter can contain the auto properties, and the second parameter can contain the setter information. For example:
To determine if the property is being set, just check for the existence of the setter. Technically, you could still use the arguments.length > 1 check, but I’ve personally never been a big fan of that syntax. By putting the value within an object it is more logical to identify the different situations. When setter is undefined, the property is not being set. When setter is defined but setter.value is undefined, the property is being set to undefined.
There would not be any naming conflicts. The object’s keys would be the entire path of the property. So, in the previous example, you would reference the properties as properties.userName and either properties.user.name or properties['user.name'], depending on the implementation.
On second thought, I think we can do this in a way that is quite compatible with existing code bases and all of the existing Ember knowledge, documentation, examples, tutorials, etc.
Let’s simply hijack the “key” parameter and turn it into an object literal containing the properties. I have never used the key parameter and don’t recall ever seeing anyone else use it. I’m sure there’s a great reason for it and is necessary in some use cases, but it seems like the exception than the rule. We can still provide the key within the object, perhaps as properties.__key to avoid naming conflicts.
The setter value can remain as is, as the second parameter. Therefore, arguments.length > 1 is still the right way to determine if the property is being set.
If this were to eventually replace the existing .property() implementation, then the only problematic code are computed properties where the key parameter is being used. Am I wrong in thinking that this is rarely if ever used? What is the use case where this is necessary?
I don’t really like this, properties.firstName is no real benefit over this.get('firstName') IMO
Actually I think that setters should be ignored for this use case - they are much rarer than normal CPs. This should be a shorthand for normal read-only CPs with the original syntax still available for more complex use cases.
@alexspeller What if the parameter name was something shorter than properties? I named it properties in this example to clarify what it is, but it can obviously be named anything.
The difference here is p.firstName as opposed to firstName. IMO significantly better than this.get('firstName'). It may not be quite as beautiful as firstName but IMO it gets you 90% of the way there (cleaner, static variable as opposed to function call) and maintains compatibility with setters.
@alexspeller and @gunn, I think we’ve got a good candidate for being added to core here, but not if it’s too ‘magic’, and probably not using eval(). I really like Christopher’s solution, it’s very clear what’s happening, it’s backwards compatible and can even be ignored if you like, and provides support for setting as well.
@simonvwade yes I’m definitely thinking about what would make sense in core ember while looking at the options.
So, I’ll favour choices with less magic, that are closer conceptually and in terms of compatibility to the existing computed properties.
I’d rather avoid the p.firstName style if we can although it sure does beat this.get(‘firstName’).
Also the suggestion as it is could run into trouble if you have keys like user and user.stats.age - user can’t just be an almost empty object, it has to be the full user instance. The p["user.stats.age"] syntax would avoid this but then we’re back to using strings and it isn’t as much of an improvement.
It also doesn’t allow backwards compatible setter syntax, or using shorter names which is a nice-to-have. It could be combined with the object literal syntax though - {name: "user.name"}...
The big question for me now is if an eval or new Function would be acceptable. If so, this syntax seems the best of all worlds:
greeting: function() {
return "Hi " + name + " your login is: " + userName;
}.auto({name: "user.name"}, "userName")
It even gives us 95% backwards compatible setters.