{"action":"created","issue":{"url":"https://api.github.com/repos/ractivejs/ractive/issues/3141","repository_url":"https://api.github.com/repos/ractivejs/ractive","labels_url":"https://api.github.com/repos/ractivejs/ractive/issues/3141/labels{/name}","comments_url":"https://api.github.com/repos/ractivejs/ractive/issues/3141/comments","events_url":"https://api.github.com/repos/ractivejs/ractive/issues/3141/events","html_url":"https://github.com/ractivejs/ractive/pull/3141","id":274285050,"number":3141,"title":"All the computed changes - WIP - RFC","user":{"login":"evs-chris","id":161978,"avatar_url":"https://avatars3.githubusercontent.com/u/161978?v=4","gravatar_id":"","url":"https://api.github.com/users/evs-chris","html_url":"https://github.com/evs-chris","followers_url":"https://api.github.com/users/evs-chris/followers","following_url":"https://api.github.com/users/evs-chris/following{/other_user}","gists_url":"https://api.github.com/users/evs-chris/gists{/gist_id}","starred_url":"https://api.github.com/users/evs-chris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/evs-chris/subscriptions","organizations_url":"https://api.github.com/users/evs-chris/orgs","repos_url":"https://api.github.com/users/evs-chris/repos","events_url":"https://api.github.com/users/evs-chris/events{/privacy}","received_events_url":"https://api.github.com/users/evs-chris/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/ractivejs/ractive/milestones/17","html_url":"https://github.com/ractivejs/ractive/milestone/17","labels_url":"https://api.github.com/repos/ractivejs/ractive/milestones/17/labels","id":2073219,"number":17,"title":"0.10","description":null,"creator":{"login":"evs-chris","id":161978,"avatar_url":"https://avatars3.githubusercontent.com/u/161978?v=4","gravatar_id":"","url":"https://api.github.com/users/evs-chris","html_url":"https://github.com/evs-chris","followers_url":"https://api.github.com/users/evs-chris/followers","following_url":"https://api.github.com/users/evs-chris/following{/other_user}","gists_url":"https://api.github.com/users/evs-chris/gists{/gist_id}","starred_url":"https://api.github.com/users/evs-chris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/evs-chris/subscriptions","organizations_url":"https://api.github.com/users/evs-chris/orgs","repos_url":"https://api.github.com/users/evs-chris/repos","events_url":"https://api.github.com/users/evs-chris/events{/privacy}","received_events_url":"https://api.github.com/users/evs-chris/received_events","type":"User","site_admin":false},"open_issues":4,"closed_issues":3,"state":"open","created_at":"2016-10-17T03:17:12Z","updated_at":"2018-01-16T04:40:35Z","due_on":null,"closed_at":null},"comments":9,"created_at":"2017-11-15T19:59:07Z","updated_at":"2018-02-07T20:46:37Z","closed_at":null,"author_association":"CONTRIBUTOR","pull_request":{"url":"https://api.github.com/repos/ractivejs/ractive/pulls/3141","html_url":"https://github.com/ractivejs/ractive/pull/3141","diff_url":"https://github.com/ractivejs/ractive/pull/3141.diff","patch_url":"https://github.com/ractivejs/ractive/pull/3141.patch"},"body":"## Description:\r\n\r\nThis addresses a handful of requests involving computeds starting from at least two years back. It also introduces a number of breaking changes, so it should probably land in a version greater than 0.9.\r\n\r\nThis PR is not complete. There are a number of open questions, mostly related to nested computations that need to be answered before the test suite can be filled out around all of these changes.\r\n\r\n### Helpers\r\n\r\nAs outlined in a number of issues, many devs simply don't like having functions in their data. I personally find it liberating to be able to call functions that I throw in there and pass around, but I can see where it would be unpleasant for some. To address that, this introduces another registry that starts with `Ractive.defaults.helpers` and is inherited by subsequent extensions and instances. This registry can technically contain anything, but it's meant to hold helper functions, like `upper(str)` and `format(date, 'YYYY/mm/dd')`. The helpers registry is added into the resolution process after prefixed references and before context traversal, so if you have a reference with a single unprefixed key, it will resolve in the helpers registry before looking through data and contexts.\r\n\r\nThis is only breaking if you happen to have a `helpers` key on one of your components, but otherwise, you can only break stuff by adding to the `helpers` intentionally.\r\n\r\n### Nested computations\r\n\r\nThis feature adds the ability to have computed members at an arbitrary keypath depth - not just at the root. It does so by breaking the current behavior of computed paths e.g. that `foo.bar` as a computed is available as `foo\\.bar` in the model and makes it available as the nested `bar` key on the `foo` object - if that object exists.\r\n\r\nAs it stands currently, the computed registry is only checked the first time a keypath is requested. If there's a match, subsequent requests will turn up the computed and if not, they'll turn up the plain model that maps to the actual data, which may not exists. I think there should probably be a knob to adjust that behavior a bit, like a computed that only applies if there's no underlying value and a computed that _always_ shadows the underlying value.\r\n\r\nBuilding on nested keypaths, this also introduces wildcard computeds, which are similar to pattern observers but with fewer limitations. A single `*` in a computed path will match _one_ key and a `**` will match multiple keys, so `foo.*.bar` will match `foo.1.bar` not `foo.1.baz.bar` and `foo.**.bar` will match `foo.1.bar` and `foo.1.baz.bar`. Multiple wildcards can be used in a single path, including `**.bar` to make `bar` available on _any_ path. __Note__ that matching wildcards is not _terribly_ expensive, but it's also not as cheap as just matching underlying data directly. If you have more than a handful of these, it's going to start having some performance overhead.\r\n\r\nNested computations receive their parent value as the first argument to their getter, followed by their keypath and any wildcard matches. I _think_ it would be useful to give them a context object somewhere in there too, but I'm not sure where or how. I leave that as an open question.\r\n\r\nAlso open is the question of how to call the setter for a nested computation. Should it be the same signature as the getter, but with the target value first? Should setting a nested value also invalidate the parent model, as that's likely what would be mutated?\r\n\r\n### Runtime addition of computeds\r\n\r\nYou can already add to the `computed` registry of a Ractive instance, but any existing references to the same keypath as the new computed won't use it until they are re-rendered. This adds a new method `compute` to the Ractive prototype that allows you to add a new computed at runtime and have any existing models that match the keypath transfer their deps to the new computed.\r\n\r\nThe answers to the questions for nested computations will also heavily affect how runtime computations behave. For instance, should there be a flag for wildcard computations that override existing models that match the path with a new computed instance at those paths?\r\n\r\n### Stringy computed syntax\r\n\r\nThe current string syntax for computeds looks something like a string template, but it's definitely not the same thing. Instead the interpolation points in the computed string are turned into `__ractive.get('...')` and a `var __ractive = this;` is added at the beginning. That string is then set as the body of a `new Function`.\r\n\r\nThis PR drops the manual conversion of the computed string and instead leverages Ractive's expression parser directly. This means that references no longer need to be wrapped in interpolation tags `${ ... }`. A function is still created at runtime.\r\n\r\n### Other stuff\r\n\r\nAs of 0.7, models would walk their constructor chain up to `Object`, `Function`, or `Array` looking for prototype property matches for child keys. This is problematic for the fairly common pattern of using `Object.create(null)` as the root of a prototype hierarchy, where there is no constructor. `Object.getPrototypeOf` is also sufficiently unsupported that it's not really a practical solution, so it's much simpler to just ad an `in` check and let the root prototypes contribute whatever they will too. This is breaking.\r\n\r\n## Try it out\r\n\r\nA build of this branch is available on npm as `@evs-chris/ractive@1.0.0-wild-compute-001` and https://cdn.jsdelivr.net/npm/@evs-chris/ractive@1.0.0-wild-compute-001/ractive.js.\r\n\r\n## Fixes the following issues:\r\n\r\n#3120, #2748, #1583\r\n\r\n## Is breaking:\r\nYep.\r\n\r\n`computed`s with a `.` require escaping if they're supposed to have a `.` in the keypath. This is more consistent with the rest of the data anyway.\r\n\r\n_All_ prototype properties are considered when checking to see if a model contains a value. This means that scoped refs are _much_ more important if you do things like `{{#each items}}{{length}}{{/each}}` where some items have a `length` and some don't. For those that don't, the `length` of the array will be returned because the parent context `items` _does_ have a _length_.\r\n\r\nThe string version of `computed` syntax changes to match regular old mustache expressions e.g. rather than `${width} * ${height}` you would write `width * height` for an `area` computed. This also opens up template string use in computeds e.g. ```'`the area is ${width * height}`'```. Notice the template string nested in a regular single quoted string. Ractive already supports template strings internally by having the parser transpile them, so the variable references are turned into ractive refs."},"comment":{"url":"https://api.github.com/repos/ractivejs/ractive/issues/comments/363904989","html_url":"https://github.com/ractivejs/ractive/pull/3141#issuecomment-363904989","issue_url":"https://api.github.com/repos/ractivejs/ractive/issues/3141","id":363904989,"user":{"login":"paulie4","id":203125,"avatar_url":"https://avatars2.githubusercontent.com/u/203125?v=4","gravatar_id":"","url":"https://api.github.com/users/paulie4","html_url":"https://github.com/paulie4","followers_url":"https://api.github.com/users/paulie4/followers","following_url":"https://api.github.com/users/paulie4/following{/other_user}","gists_url":"https://api.github.com/users/paulie4/gists{/gist_id}","starred_url":"https://api.github.com/users/paulie4/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paulie4/subscriptions","organizations_url":"https://api.github.com/users/paulie4/orgs","repos_url":"https://api.github.com/users/paulie4/repos","events_url":"https://api.github.com/users/paulie4/events{/privacy}","received_events_url":"https://api.github.com/users/paulie4/received_events","type":"User","site_admin":false},"created_at":"2018-02-07T20:46:37Z","updated_at":"2018-02-07T20:46:37Z","author_association":"CONTRIBUTOR","body":"@evs-chris, thanks for fixing #3169. Does that mean this can get merged back in the mainline branch soon, hopefully before 1.0 is released?"}}
{"actor":{"display_login":"paulie4"}}