{"action":"created","comment":{"url":"https://api.github.com/repos/tomdale/rfcs/pulls/comments/236418036","pull_request_review_id":178490379,"id":236418036,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNjQxODAzNg==","diff_hunk":"@@ -227,34 +248,350 @@ memoization) if a property shows up as a bottleneck during profiling.\n \n ### Native Classes\n \n-Tracked properties are designed from the ground up for native JavaScript (ES6) classes.\n-\n-### \"Just JavaScript\"\n-\n+Tracked properties are designed from the ground up for native JavaScript (ES6)\n+classes. Adopting them will make it easier to convert away from the aging Ember\n+object-model, and\n \n ## Detailed design\n \n ### Autotracking Stack\n-> This is the bulk of the RFC.\n \n-> Explain the design in enough detail for somebody\n-familiar with the framework to understand, and for somebody familiar with the\n-implementation to implement. This should get into specifics and corner-cases,\n-and include examples of how the feature is used. Any new terminology should be\n-defined here.\n+Autotracking works by taking advantage of the fact that all mutable properties\n+will be decorated by `tracked`. This decorator adds a getter and a setter that\n+wraps the property, be it a tracked getter or a tracked simple property. Every\n+time we activate a tracked getter, it creates a global stack before it begins\n+executing the getter's code, and as it executes, any other tracked wrappers\n+that run add themselves to the stack.\n+\n+This example demonstrates how this method works, but is simplified to only work\n+with tracked getters, and to only track keys. It uses the stage 2 decorator\n+syntax.\n+\n+```js\n+let PROPERTY_STACK;\n+\n+function tracked({ key, descriptor }) {\n+  let originalGet = descriptor.get;\n+\n+  descriptor.get = function() {\n+    // store the stack from the previous getter, if it exists\n+    let previousStack = PROPERTY_STACK;\n+\n+    // create a new stack\n+    PROPERTY_STACK = [];\n+\n+    // call the original get function to get the value\n+    let value = originalGet.call(this);\n+\n+    // At this point, PROPERTY_STACK will contain all of the keys of any values\n+    // that were marked as tracked and accessed when `originalGet` was called.\n+    // We can use this knowledge to setup our change tracking, then push our\n+    // own key and the current stack into the previous stack if it exists.\n+    if (previousStack) {\n+      previousStack.push(key, ...PROPERTY_STACK);\n+    }\n+\n+    PROPERTY_STACK = previousStack;\n+\n+    return value;\n+  }\n+}\n+```\n+\n+Of course, tracking keys would only work if we knew the full paths, or were only\n+concerned with only changes to the current object. The real implementation uses\n+[Glimmer VM's internal validators](https://github.com/glimmerjs/glimmer-vm/blob/master/guides/05-validators.md)\n+to track changes to properties.\n+\n+As properties are accessed, their validation  _tags_ are pushed on the stack,\n+and when the property access is complete these tags are combined into a single\n+tag for that property. Setters are added to tracked getters and tracked simple\n+properties as appropriate which manually dirty these tags whenever they are\n+activated, and subsequently invalidate all of the tags for any getters which\n+ever accessed them.\n+\n+### Untracked Properties\n+\n+Revisiting the `middleName` bug from earlier, you may notice that it's still\n+possible for a new developer to introduce a bug with tracked properties. What if\n+they don't know that all mutable properties must be tracked? This would fail,\n+just like our previous example:\n+\n+```js\n+import { tracked } from '@ember/object';\n+\n+class Person {\n+  @tracked firstName = 'Tom';\n+  @tracked lastName = 'Dale';\n+  middleName = 'Tomster';\n+\n+  @tracked\n+  get fullName() {\n+    return `${this.firstName} ${this.middleName} ${this.lastName}`;\n+  }\n+}\n+```\n+\n+The last piece of the puzzle here will be a development time only assertion.\n+When you access _untracked_ properties from an a tracked getter, we will install\n+something similar to the mandatory setter function. This will throw an error if\n+the user ever tries to set the property, effectively making it immutable.\n+\n+To accomplish this, we can wrap any object which has tracked properties with a\n+native [`Proxy`][proxy], which will subsequently wrap any objects or array properties\n+which are accessed with another proxy, and so on. These proxies will be able\n+to check if any untracked properties are being accessed from an tracked context,\n+and setup immutable assertions if so.\n+\n+### Tracked Interop with Computed Properties and `Ember.set`\n+\n+Tracked properties work well in isolation. In systems built from the ground up\n+using `@tracked`, all mutable values will be marked as tracked, so autotracking\n+will be able to \"watch\" them all as they are accessed and changed.\n+\n+However, tracked properties will have to interoperate with the current system\n+for tracking changes and mutable state in Ember for some time as the community\n+transitions forward. The origin of all mutations in the old system is through\n+`Ember.set` (or more specifically, `notifyPropertyChange`), but there are many\n+ways to listen to these changes. Tracked properties are concerned with two\n+specific cases:\n+\n+1. **Computed Properties.** When `notifyPropertyChange` is called on one of a\n+computed property's dependencies, it will invalidate, and invalidate any computed properties which\n+depend on it. In this way, one call to `set` can invalidate many properties in\n+the system. Tracked properties must also properly invalidate if they consume\n+any of these properties.\n+2. **Ember.set() directly**. If a tracked property accesses a simple, untracked\n+property which is _later_ set with `Ember.set()`, it must have a way to know\n+that this property has changed. This can occur in services, helper classes which\n+have not yet converted to `@tracked`, and in many other edge cases.\n+\n+In the case of computed properties, we have all the information we need up front\n+to allow tracked getters to watch for changes in the computed. They will notify\n+whenever they are updated, so the same technique which is used for the autotrack\n+stack can be used to watch them as well. Crucially, computed properties will\n+always have a tag due to the fact that they are defined explicitly in class\n+definitions, which means we have something to watch the very first time a\n+computed property is accessed.\n+\n+In the edge case of non-computed, non-tracked property access, this is not the\n+case. The property may be completely undecorated, meaning it may never have had\n+a tag in the first place. With standard Javascript property access, we won't\n+have an opportunity to _add_ a tag either. Autotrack will not work in these\n+cases:\n+\n+```js\n+const Config = Service.extend({\n+  polling: {\n+    shouldPoll: false,\n+    pollInterval: -1,\n+  },\n+\n+  init() {\n+    this._super(...arguments);\n+\n+    fetch('config/api/url')\n+      .then(r => r.json())\n+      .then(polling => set(this, 'polling', polling));\n+  }\n+})\n+\n+class SomeComponent extends Component {\n+  @service config;\n+\n+  @tracked\n+  get pollInterval() {\n+    // When we access shouldPoll and pollInterval, they don't have tags or\n+    // wrapped getters. We have no idea that they could update in the future.\n+    let { shouldPoll, pollInterval } = this.config.polling;\n+\n+    return shouldPoll ? pollInterval : -1;\n+  }\n+}\n+```\n+\n+To support this case, `Ember.get` will manually opt in to adding the tags for\n+any properties which are accessed during a tracked getter. This allows tracked\n+getters to safely access services and objects which are still using the legacy\n+change tracking system:\n+\n+```js\n+const Config = Service.extend({\n+  polling: {\n+    shouldPoll: false,\n+    pollInterval: -1,\n+  },\n+\n+  init() {\n+    this._super(...arguments);\n+\n+    fetch('config/api/url')\n+      .then(r => r.json())\n+      .then(polling => set(this, 'polling', polling));\n+  }\n+})\n+\n+class SomeComponent extends Component {\n+  @service config;\n+\n+  @tracked\n+  get pollInterval() {\n+    // When we access shouldPoll and pollInterval, they don't have tags or\n+    // wrapped getters. We have no idea that they could update in the future.\n+    let shouldPoll = this.get('config.polling.shouldPoll');\n+    let pollInterval = this.get('config.polling.pollInterval');\n+\n+    return shouldPoll ? pollInterval : -1;\n+  }\n+}\n+```\n+\n+This solution will _only_ be necessary in cases where users do not know ahead of\n+time whether a property is A. immutable, B. tracked, or C. a computed property.\n+As time goes on and more and more services, addons, and apps are converted to\n+`@tracked`, the need for `Ember.get` will become smaller and smaller, until it\n+can be removed from the framework entirely (along with `Ember.set`).\n+\n+### Computed Property Interop with Tracked\n+\n+The previous section covers the cases where classic Ember properties are\n+accessed from tracked properties, but we also have to define the behavior for\n+when the opposite occurs: tracked properties are accessed by from a classic\n+context.\n+\n+There are two major ways this can occur:\n+\n+1. Computed Properties\n+2. Observers\n+\n+Observers are strictly within the old paradigm, so tracked properties will _not_\n+attempt to interoperate with them. Using `Ember.set` on a tracked property will\n+still notify that the property has updated, and any observers for that property\n+will fire accordingly.\n+\n+Computed properties, on the other hand, are similar enough to tracked properties\n+that interoperability would be useful. As such, computed properties will _also_\n+automatically add tracked properties to their dependencies as they are accessed.\n+This will use the same mechanism as tracked properties do with the autotrack\n+stack, and will allow users to progressively convert from computed properties to\n+tracked properties over time by whittling away at the explicit dependencies\n+within a CP.\n+\n+### Interop with EmberObject\n+\n+While `@tracked` is designed from the ground up for native Javascript classes,\n+it will have to interoperate with legacy code for some time. In the previous\n+sections, we noted how `get` would have to be used with any legacy code which\n+has not been updated to tracked properties. As such, to make the transition as\n+easy as possible, `tracked` will also be made to work with the classic\n+object-model:\n+\n+```js\n+const Config = Service.extend({\n+  polling: tracked({\n+    value: {\n+      shouldPoll: false,\n+      pollInterval: -1,\n+    }\n+  }),\n+\n+  init() {\n+    this._super(...arguments);\n+\n+    fetch('config/api/url')\n+      .then(r => r.json())\n+      .then(polling => set(this, 'polling', polling));\n+  }\n+})\n+```\n+\n+Tracked getters and setters will also be allowed, with the `get` and `set` keys\n+on the passed in configuration. This will allow existing libraries to transition\n+incrementally, and add tracked support minimally where necessary.\n+\n+This form will _not_ be allowed on native classes, and will hard error if it is\n+attempted. Additionally, default values will be defined on the _prototype_ to\n+maintain consistency with the classic object-model.\n \n ## How we teach this\n \n-> What names and terminology work best for these concepts and why? How is this\n-idea best presented? As a continuation of existing Ember patterns, or as a\n-wholly new one?\n+There are two different aspects of tracked properties which need to be\n+considered for the learning story:\n+\n+1. **General usage.** Which properties should I mark as tracked? How do I\n+consume them? How do I trigger changes?\n+2. **Interop with legacy systems.** How do I safely consume tracked properties\n+from legacy classes and computeds? How do I safely consume legacy APIs from\n+tracked properties?\n+\n+### General Usage\n+\n+The mental model with tracked properties is that anything _mutable_ should be\n+tracked. If a value will ever change, it should have the `@tracked` decorator\n+attached to it.\n+\n+After that, usage should be \"Just Javascript\". You can safely access values\n+using any syntax you like, including desctructuring, and you can update values\n+using standard assignments.\n+\n+```js\n+// Dot notation\n+const fullName = person.fullName;\n+// Destructuring\n+const { fullName } = person;\n+// Bracket notation for computed property names\n+const fullName = person['fullName'];\n+\n+// Simple assignment\n+this.firstName = \"Yehuda\";\n+// Addition assignment (+=)\n+this.lastName += \"Katz\";\n+// Increment operator\n+this.age++;\n+```\n+\n+#### Triggering Updates on Complex Objects\n \n-> Would the acceptance of this proposal mean the Ember guides must be\n-re-organized or altered? Does it change how Ember is taught to new users\n-at any level?\n+There may be cases where users want to update values in complex, untracked\n+objects such as arrays or POJOs. `@tracked` will only be usable with class\n+syntax at first, and while it may make sense to formalize these objects into\n+tracked classes in some cases, this will not always be the case.\n \n-> How should this feature be introduced and taught to existing Ember\n-users?\n+To do this, users can re-set a tracked value directly after its inner values\n+have been updated.\n+\n+```js\n+class SomeComponent extends Component {\n+  @tracked items = [];\n+\n+  @action\n+  pushItem(item) {\n+    let { items } = this;\n+\n+    items.push(item);\n+\n+    this.items = items;\n+  }\n+}\n+```\n+\n+### Interop with Legacy Systems\n+\n+There are two cases, as discussed in the Detailed Design section, that we need\n+to consider when teaching interoperability:\n+\n+1. Tracked getters accessing non-tracked properies and computeds","path":"text/0000-tracked-properties.md","position":407,"original_position":407,"commit_id":"20246fda1876d4c2f686b80f4988da93e97a2313","original_commit_id":"20246fda1876d4c2f686b80f4988da93e97a2313","user":{"login":"tomdale","id":90888,"node_id":"MDQ6VXNlcjkwODg4","avatar_url":"https://avatars2.githubusercontent.com/u/90888?v=4","gravatar_id":"","url":"https://api.github.com/users/tomdale","html_url":"https://github.com/tomdale","followers_url":"https://api.github.com/users/tomdale/followers","following_url":"https://api.github.com/users/tomdale/following{/other_user}","gists_url":"https://api.github.com/users/tomdale/gists{/gist_id}","starred_url":"https://api.github.com/users/tomdale/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomdale/subscriptions","organizations_url":"https://api.github.com/users/tomdale/orgs","repos_url":"https://api.github.com/users/tomdale/repos","events_url":"https://api.github.com/users/tomdale/events{/privacy}","received_events_url":"https://api.github.com/users/tomdale/received_events","type":"User","site_admin":false},"body":"s/properies/properties/","created_at":"2018-11-26T20:41:26Z","updated_at":"2018-11-26T20:41:27Z","html_url":"https://github.com/tomdale/rfcs/pull/4#discussion_r236418036","pull_request_url":"https://api.github.com/repos/tomdale/rfcs/pulls/4","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/comments/236418036"},"html":{"href":"https://github.com/tomdale/rfcs/pull/4#discussion_r236418036"},"pull_request":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/4"}}},"pull_request":{"url":"https://api.github.com/repos/tomdale/rfcs/pulls/4","id":232827476,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMyODI3NDc2","html_url":"https://github.com/tomdale/rfcs/pull/4","diff_url":"https://github.com/tomdale/rfcs/pull/4.diff","patch_url":"https://github.com/tomdale/rfcs/pull/4.patch","issue_url":"https://api.github.com/repos/tomdale/rfcs/issues/4","number":4,"state":"open","locked":false,"title":"add detailed design, how we teach, and alternatives","user":{"login":"pzuraq","id":685518,"node_id":"MDQ6VXNlcjY4NTUxOA==","avatar_url":"https://avatars3.githubusercontent.com/u/685518?v=4","gravatar_id":"","url":"https://api.github.com/users/pzuraq","html_url":"https://github.com/pzuraq","followers_url":"https://api.github.com/users/pzuraq/followers","following_url":"https://api.github.com/users/pzuraq/following{/other_user}","gists_url":"https://api.github.com/users/pzuraq/gists{/gist_id}","starred_url":"https://api.github.com/users/pzuraq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pzuraq/subscriptions","organizations_url":"https://api.github.com/users/pzuraq/orgs","repos_url":"https://api.github.com/users/pzuraq/repos","events_url":"https://api.github.com/users/pzuraq/events{/privacy}","received_events_url":"https://api.github.com/users/pzuraq/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-11-22T00:06:37Z","updated_at":"2018-11-26T20:41:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"40dfd30a4ddd5c2140b32c92ef6062266cb853f1","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/tomdale/rfcs/pulls/4/commits","review_comments_url":"https://api.github.com/repos/tomdale/rfcs/pulls/4/comments","review_comment_url":"https://api.github.com/repos/tomdale/rfcs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/tomdale/rfcs/issues/4/comments","statuses_url":"https://api.github.com/repos/tomdale/rfcs/statuses/20246fda1876d4c2f686b80f4988da93e97a2313","head":{"label":"pzuraq:tracked-properties","ref":"tracked-properties","sha":"20246fda1876d4c2f686b80f4988da93e97a2313","user":{"login":"pzuraq","id":685518,"node_id":"MDQ6VXNlcjY4NTUxOA==","avatar_url":"https://avatars3.githubusercontent.com/u/685518?v=4","gravatar_id":"","url":"https://api.github.com/users/pzuraq","html_url":"https://github.com/pzuraq","followers_url":"https://api.github.com/users/pzuraq/followers","following_url":"https://api.github.com/users/pzuraq/following{/other_user}","gists_url":"https://api.github.com/users/pzuraq/gists{/gist_id}","starred_url":"https://api.github.com/users/pzuraq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pzuraq/subscriptions","organizations_url":"https://api.github.com/users/pzuraq/orgs","repos_url":"https://api.github.com/users/pzuraq/repos","events_url":"https://api.github.com/users/pzuraq/events{/privacy}","received_events_url":"https://api.github.com/users/pzuraq/received_events","type":"User","site_admin":false},"repo":{"id":98708887,"node_id":"MDEwOlJlcG9zaXRvcnk5ODcwODg4Nw==","name":"emberjs-rfcs","full_name":"pzuraq/emberjs-rfcs","private":false,"owner":{"login":"pzuraq","id":685518,"node_id":"MDQ6VXNlcjY4NTUxOA==","avatar_url":"https://avatars3.githubusercontent.com/u/685518?v=4","gravatar_id":"","url":"https://api.github.com/users/pzuraq","html_url":"https://github.com/pzuraq","followers_url":"https://api.github.com/users/pzuraq/followers","following_url":"https://api.github.com/users/pzuraq/following{/other_user}","gists_url":"https://api.github.com/users/pzuraq/gists{/gist_id}","starred_url":"https://api.github.com/users/pzuraq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pzuraq/subscriptions","organizations_url":"https://api.github.com/users/pzuraq/orgs","repos_url":"https://api.github.com/users/pzuraq/repos","events_url":"https://api.github.com/users/pzuraq/events{/privacy}","received_events_url":"https://api.github.com/users/pzuraq/received_events","type":"User","site_admin":false},"html_url":"https://github.com/pzuraq/emberjs-rfcs","description":"RFCs for changes to Ember","fork":true,"url":"https://api.github.com/repos/pzuraq/emberjs-rfcs","forks_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/forks","keys_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/teams","hooks_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/hooks","issue_events_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/events","assignees_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/tags","blobs_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/languages","stargazers_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/stargazers","contributors_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/contributors","subscribers_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/subscribers","subscription_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/subscription","commits_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/merges","archive_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/downloads","issues_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/labels{/name}","releases_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/pzuraq/emberjs-rfcs/deployments","created_at":"2017-07-29T04:44:44Z","updated_at":"2018-08-31T16:09:33Z","pushed_at":"2018-11-26T20:41:26Z","git_url":"git://github.com/pzuraq/emberjs-rfcs.git","ssh_url":"git@github.com:pzuraq/emberjs-rfcs.git","clone_url":"https://github.com/pzuraq/emberjs-rfcs.git","svn_url":"https://github.com/pzuraq/emberjs-rfcs","homepage":null,"size":765,"stargazers_count":0,"watchers_count":0,"language":"Shell","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":3,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"tomdale:tracked-properties","ref":"tracked-properties","sha":"80e98e1f3b9be5818efe0bedb3139d9a78e5dd07","user":{"login":"tomdale","id":90888,"node_id":"MDQ6VXNlcjkwODg4","avatar_url":"https://avatars2.githubusercontent.com/u/90888?v=4","gravatar_id":"","url":"https://api.github.com/users/tomdale","html_url":"https://github.com/tomdale","followers_url":"https://api.github.com/users/tomdale/followers","following_url":"https://api.github.com/users/tomdale/following{/other_user}","gists_url":"https://api.github.com/users/tomdale/gists{/gist_id}","starred_url":"https://api.github.com/users/tomdale/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomdale/subscriptions","organizations_url":"https://api.github.com/users/tomdale/orgs","repos_url":"https://api.github.com/users/tomdale/repos","events_url":"https://api.github.com/users/tomdale/events{/privacy}","received_events_url":"https://api.github.com/users/tomdale/received_events","type":"User","site_admin":false},"repo":{"id":25714304,"node_id":"MDEwOlJlcG9zaXRvcnkyNTcxNDMwNA==","name":"rfcs","full_name":"tomdale/rfcs","private":false,"owner":{"login":"tomdale","id":90888,"node_id":"MDQ6VXNlcjkwODg4","avatar_url":"https://avatars2.githubusercontent.com/u/90888?v=4","gravatar_id":"","url":"https://api.github.com/users/tomdale","html_url":"https://github.com/tomdale","followers_url":"https://api.github.com/users/tomdale/followers","following_url":"https://api.github.com/users/tomdale/following{/other_user}","gists_url":"https://api.github.com/users/tomdale/gists{/gist_id}","starred_url":"https://api.github.com/users/tomdale/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomdale/subscriptions","organizations_url":"https://api.github.com/users/tomdale/orgs","repos_url":"https://api.github.com/users/tomdale/repos","events_url":"https://api.github.com/users/tomdale/events{/privacy}","received_events_url":"https://api.github.com/users/tomdale/received_events","type":"User","site_admin":false},"html_url":"https://github.com/tomdale/rfcs","description":"RFCs for changes to Ember","fork":true,"url":"https://api.github.com/repos/tomdale/rfcs","forks_url":"https://api.github.com/repos/tomdale/rfcs/forks","keys_url":"https://api.github.com/repos/tomdale/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tomdale/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tomdale/rfcs/teams","hooks_url":"https://api.github.com/repos/tomdale/rfcs/hooks","issue_events_url":"https://api.github.com/repos/tomdale/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/tomdale/rfcs/events","assignees_url":"https://api.github.com/repos/tomdale/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/tomdale/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/tomdale/rfcs/tags","blobs_url":"https://api.github.com/repos/tomdale/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tomdale/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tomdale/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/tomdale/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tomdale/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/tomdale/rfcs/languages","stargazers_url":"https://api.github.com/repos/tomdale/rfcs/stargazers","contributors_url":"https://api.github.com/repos/tomdale/rfcs/contributors","subscribers_url":"https://api.github.com/repos/tomdale/rfcs/subscribers","subscription_url":"https://api.github.com/repos/tomdale/rfcs/subscription","commits_url":"https://api.github.com/repos/tomdale/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/tomdale/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/tomdale/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/tomdale/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/tomdale/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/tomdale/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tomdale/rfcs/merges","archive_url":"https://api.github.com/repos/tomdale/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tomdale/rfcs/downloads","issues_url":"https://api.github.com/repos/tomdale/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/tomdale/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/tomdale/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/tomdale/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tomdale/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/tomdale/rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/tomdale/rfcs/deployments","created_at":"2014-10-25T00:27:31Z","updated_at":"2018-10-31T16:02:15Z","pushed_at":"2018-11-26T20:00:27Z","git_url":"git://github.com/tomdale/rfcs.git","ssh_url":"git@github.com:tomdale/rfcs.git","clone_url":"https://github.com/tomdale/rfcs.git","svn_url":"https://github.com/tomdale/rfcs","homepage":null,"size":696,"stargazers_count":27,"watchers_count":27,"language":"Shell","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":7,"mirror_url":null,"archived":false,"open_issues_count":2,"license":null,"forks":7,"open_issues":2,"watchers":27,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/4"},"html":{"href":"https://github.com/tomdale/rfcs/pull/4"},"issue":{"href":"https://api.github.com/repos/tomdale/rfcs/issues/4"},"comments":{"href":"https://api.github.com/repos/tomdale/rfcs/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/tomdale/rfcs/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/tomdale/rfcs/statuses/20246fda1876d4c2f686b80f4988da93e97a2313"}},"author_association":"NONE"}}
{"actor":{"display_login":"tomdale"}}