{"action":"created","comment":{"url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/comments/23886639","id":23886639,"diff_hunk":"@@ -0,0 +1,340 @@\n+'use strict';\n+\n+var assign = require('object-assign');\n+var invariant = require('tuxx/src/TuxxInvariant');\n+\n+// buildNewState FUNCTION: internal function that creates an updated state object by invoking callback on the deepest keys of the newProps object, throwing an error if any of the keys in newProps dont match the corresponding keys in currentState\n+// @param currentState OBJECT: a shallow copy of this.state\n+// @param newProps OBJECT: an object holding keys that should match those in currentState and whose values will each be passed to callback\n+// @param callback FUNCTION: optional callback argument that will be executed once setState is completed and the component is re-rendered\n+var buildNewState = function (currentState, newProps, callback) {\n+  // create empty object which will be used to build the eventual state object that will be passed in to setState after our callback has been invoked on the proper deep keys\n+  // NOTE: React's setState method makes a shallow copy of the keys at the top-most level, so we don't need to make a shallow copy of the top-most keys here, but rather, make shallow copies of keys at every level EXCEPT for the top\n+  var newState = {};\n+\n+  // recurseKeys FUNCTION: traverses through keys in a heavily nested object, building our newState object as we iterate through potential nested objects and setting the deepest keys passed by newProps to be the result of invoking our callback on the deepest keys in the newProps and corresponding currentState keys\n+  // @param currentST OBJECT: properties of the currentState\n+  // @param newST OBJECT: properties of the newState\n+  // @param newPR OBJECT: properties of the newProps\n+  var recurseKeys = function (currentSt, newSt, newPr) {\n+    for (var key in newPr) {\n+      if (newPr.hasOwnProperty(key)) {\n+        var valAtKey = newPr[key];\n+        var stAtKey = currentSt[key];\n+        // if the value at this newProps key is defined and is an object, and if it matches the corresponding key in the current state which is also defined and is an object\n+        if (valAtKey !== null && typeof valAtKey === 'object' && !Array.isArray(valAtKey) && stAtKey !== null && typeof stAtKey === 'object' && !Array.isArray(stAtKey)) {\n+          // shallow copy the keys from the currentState into our newState so that there are no references to the currentState keys, but our newState contains identical values\n+          newSt[key] = assign({}, stAtKey);\n+          // invoke recurseKeys with the nested objects inside the current key\n+          recurseKeys(stAtKey, newSt[key], valAtKey);\n+        } else {\n+          // we have found a deep key in newProps and all keys match so far, so we invoke our callback, which sets the result of invoking our callback with these deep keys to be the value at the corresponding key in our newState\n+          callback(newSt, key, stAtKey, valAtKey);\n+        }\n+      }\n+    }\n+  };\n+  // invoke recurseKeys and build newState object by passing in the currentState, our empty newState, and newProps\n+  recurseKeys(currentState, newState, newProps);\n+  return newState;\n+};\n+\n+// invariantNumberCheck FUNCTION: internal function that ensures a given value is a number or throws an error\n+// @param input UNKNOWN: value we are performing invariant check upon\n+var invariantNumberCheck = function (input) {\n+  if (typeof(input) !== \"number\") {\n+    invariant(!input, 'Cannot perform operation on \"%s\" because it is not of type number.', input);\n+  };\n+};\n+\n+// invariantArrayCheck FUNCTION: internal function that ensures a given value is an array or throws an error\n+// @param input UNKNOWN: value we are performing invariant check upon\n+var invariantArrayCheck = function (input) {\n+  if (!Array.isArray(input)) {\n+    invariant(!input, 'Cannot perform operation on \"%s\" because it is not an array.', input);\n+  };\n+};\n+\n+// invariantValueCheck FUNCTION: internal function that ensures a given value is not stored in an array or an object\n+// @param input UNKNOWN: value we are performing invariant check upon\n+var invariantValueCheck = function (input) {\n+  if (typeof(input) === \"object\") {\n+    invariant(!input, 'Cannot perform operation on \"%s\" because it must not be an array or object.', input);\n+  }\n+};\n+\n+// invariantNumberOrStringCheck FUNCTION: internal function that ensures a given value is a number or string or throws an error\n+// @param input UNKNOWN: value we are performing invariant check upon\n+var invariantNumberOrStringCheck = function (input) {\n+  if (typeof(input) !== \"number\" && typeof(input) !== \"string\") {\n+    invariant(!input, 'Cannot perform operation on \"%s\" because it is not of type number or of type string.', input);\n+  };\n+};\n+\n+// invariantArgCheck FUNCTION: internal function that ensures a given value is defined and is an object or throws an error\n+// @param input UNKNOWN: value we are performing invariant check upon\n+var invariantArgCheck = function (input) {\n+  if (!input) {\n+    invariant(input, 'This function requires an object as an argument.');\n+  } else if (Object.prototype.toString.call(input) !== '[object Object]') {\n+    invariant(!input, 'This function requires an object as an argument.');\n+  }\n+};\n+\n+// mixin to React class that will provide convenience methods for updating state\n+module.exports = {\n+  // addState FUNCTION: adds the values of the deepest keys in the passed in object to the corresponding deepest keys in the current state, or throws an error if keys don't match\n+  // @param propsToAdd OBJECT: required object argument where the deepest keys are numbers or strings","path":"src/TuxxState.js","position":87,"original_position":87,"commit_id":"2a1f75d37f0d0c4179f0c6766b66832df101e988","original_commit_id":"2a1f75d37f0d0c4179f0c6766b66832df101e988","user":{"login":"Cheerazar","id":5581107,"avatar_url":"https://avatars.githubusercontent.com/u/5581107?v=3","gravatar_id":"","url":"https://api.github.com/users/Cheerazar","html_url":"https://github.com/Cheerazar","followers_url":"https://api.github.com/users/Cheerazar/followers","following_url":"https://api.github.com/users/Cheerazar/following{/other_user}","gists_url":"https://api.github.com/users/Cheerazar/gists{/gist_id}","starred_url":"https://api.github.com/users/Cheerazar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Cheerazar/subscriptions","organizations_url":"https://api.github.com/users/Cheerazar/orgs","repos_url":"https://api.github.com/users/Cheerazar/repos","events_url":"https://api.github.com/users/Cheerazar/events{/privacy}","received_events_url":"https://api.github.com/users/Cheerazar/received_events","type":"User","site_admin":false},"body":"Can the deepest keys be arrays?","created_at":"2015-01-31T05:00:49Z","updated_at":"2015-01-31T05:00:49Z","html_url":"https://github.com/TuxedoJS/TuxedoJS/pull/110#discussion_r23886639","pull_request_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110","_links":{"self":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/comments/23886639"},"html":{"href":"https://github.com/TuxedoJS/TuxedoJS/pull/110#discussion_r23886639"},"pull_request":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110"}}},"pull_request":{"url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110","id":27487360,"html_url":"https://github.com/TuxedoJS/TuxedoJS/pull/110","diff_url":"https://github.com/TuxedoJS/TuxedoJS/pull/110.diff","patch_url":"https://github.com/TuxedoJS/TuxedoJS/pull/110.patch","issue_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/110","number":110,"state":"open","locked":false,"title":"Implement TuxState convenience methods and corresponding tests.","user":{"login":"plauer","id":4451274,"avatar_url":"https://avatars.githubusercontent.com/u/4451274?v=3","gravatar_id":"","url":"https://api.github.com/users/plauer","html_url":"https://github.com/plauer","followers_url":"https://api.github.com/users/plauer/followers","following_url":"https://api.github.com/users/plauer/following{/other_user}","gists_url":"https://api.github.com/users/plauer/gists{/gist_id}","starred_url":"https://api.github.com/users/plauer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plauer/subscriptions","organizations_url":"https://api.github.com/users/plauer/orgs","repos_url":"https://api.github.com/users/plauer/repos","events_url":"https://api.github.com/users/plauer/events{/privacy}","received_events_url":"https://api.github.com/users/plauer/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-01-16T06:53:05Z","updated_at":"2015-01-31T05:00:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3e9075a55683adade83780e6dbd507710e5011b7","assignee":{"login":"plauer","id":4451274,"avatar_url":"https://avatars.githubusercontent.com/u/4451274?v=3","gravatar_id":"","url":"https://api.github.com/users/plauer","html_url":"https://github.com/plauer","followers_url":"https://api.github.com/users/plauer/followers","following_url":"https://api.github.com/users/plauer/following{/other_user}","gists_url":"https://api.github.com/users/plauer/gists{/gist_id}","starred_url":"https://api.github.com/users/plauer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plauer/subscriptions","organizations_url":"https://api.github.com/users/plauer/orgs","repos_url":"https://api.github.com/users/plauer/repos","events_url":"https://api.github.com/users/plauer/events{/privacy}","received_events_url":"https://api.github.com/users/plauer/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110/commits","review_comments_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110/comments","review_comment_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/comments/{number}","comments_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/110/comments","statuses_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/statuses/2a1f75d37f0d0c4179f0c6766b66832df101e988","head":{"label":"plauer:stateMethods","ref":"stateMethods","sha":"2a1f75d37f0d0c4179f0c6766b66832df101e988","user":{"login":"plauer","id":4451274,"avatar_url":"https://avatars.githubusercontent.com/u/4451274?v=3","gravatar_id":"","url":"https://api.github.com/users/plauer","html_url":"https://github.com/plauer","followers_url":"https://api.github.com/users/plauer/followers","following_url":"https://api.github.com/users/plauer/following{/other_user}","gists_url":"https://api.github.com/users/plauer/gists{/gist_id}","starred_url":"https://api.github.com/users/plauer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plauer/subscriptions","organizations_url":"https://api.github.com/users/plauer/orgs","repos_url":"https://api.github.com/users/plauer/repos","events_url":"https://api.github.com/users/plauer/events{/privacy}","received_events_url":"https://api.github.com/users/plauer/received_events","type":"User","site_admin":false},"repo":{"id":28932092,"name":"TuxedoJS","full_name":"plauer/TuxedoJS","owner":{"login":"plauer","id":4451274,"avatar_url":"https://avatars.githubusercontent.com/u/4451274?v=3","gravatar_id":"","url":"https://api.github.com/users/plauer","html_url":"https://github.com/plauer","followers_url":"https://api.github.com/users/plauer/followers","following_url":"https://api.github.com/users/plauer/following{/other_user}","gists_url":"https://api.github.com/users/plauer/gists{/gist_id}","starred_url":"https://api.github.com/users/plauer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plauer/subscriptions","organizations_url":"https://api.github.com/users/plauer/orgs","repos_url":"https://api.github.com/users/plauer/repos","events_url":"https://api.github.com/users/plauer/events{/privacy}","received_events_url":"https://api.github.com/users/plauer/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/plauer/TuxedoJS","description":"","fork":true,"url":"https://api.github.com/repos/plauer/TuxedoJS","forks_url":"https://api.github.com/repos/plauer/TuxedoJS/forks","keys_url":"https://api.github.com/repos/plauer/TuxedoJS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/plauer/TuxedoJS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/plauer/TuxedoJS/teams","hooks_url":"https://api.github.com/repos/plauer/TuxedoJS/hooks","issue_events_url":"https://api.github.com/repos/plauer/TuxedoJS/issues/events{/number}","events_url":"https://api.github.com/repos/plauer/TuxedoJS/events","assignees_url":"https://api.github.com/repos/plauer/TuxedoJS/assignees{/user}","branches_url":"https://api.github.com/repos/plauer/TuxedoJS/branches{/branch}","tags_url":"https://api.github.com/repos/plauer/TuxedoJS/tags","blobs_url":"https://api.github.com/repos/plauer/TuxedoJS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/plauer/TuxedoJS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/plauer/TuxedoJS/git/refs{/sha}","trees_url":"https://api.github.com/repos/plauer/TuxedoJS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/plauer/TuxedoJS/statuses/{sha}","languages_url":"https://api.github.com/repos/plauer/TuxedoJS/languages","stargazers_url":"https://api.github.com/repos/plauer/TuxedoJS/stargazers","contributors_url":"https://api.github.com/repos/plauer/TuxedoJS/contributors","subscribers_url":"https://api.github.com/repos/plauer/TuxedoJS/subscribers","subscription_url":"https://api.github.com/repos/plauer/TuxedoJS/subscription","commits_url":"https://api.github.com/repos/plauer/TuxedoJS/commits{/sha}","git_commits_url":"https://api.github.com/repos/plauer/TuxedoJS/git/commits{/sha}","comments_url":"https://api.github.com/repos/plauer/TuxedoJS/comments{/number}","issue_comment_url":"https://api.github.com/repos/plauer/TuxedoJS/issues/comments/{number}","contents_url":"https://api.github.com/repos/plauer/TuxedoJS/contents/{+path}","compare_url":"https://api.github.com/repos/plauer/TuxedoJS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/plauer/TuxedoJS/merges","archive_url":"https://api.github.com/repos/plauer/TuxedoJS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/plauer/TuxedoJS/downloads","issues_url":"https://api.github.com/repos/plauer/TuxedoJS/issues{/number}","pulls_url":"https://api.github.com/repos/plauer/TuxedoJS/pulls{/number}","milestones_url":"https://api.github.com/repos/plauer/TuxedoJS/milestones{/number}","notifications_url":"https://api.github.com/repos/plauer/TuxedoJS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/plauer/TuxedoJS/labels{/name}","releases_url":"https://api.github.com/repos/plauer/TuxedoJS/releases{/id}","created_at":"2015-01-07T20:17:15Z","updated_at":"2015-01-30T23:57:03Z","pushed_at":"2015-01-31T00:01:46Z","git_url":"git://github.com/plauer/TuxedoJS.git","ssh_url":"git@github.com:plauer/TuxedoJS.git","clone_url":"https://github.com/plauer/TuxedoJS.git","svn_url":"https://github.com/plauer/TuxedoJS","homepage":null,"size":881,"stargazers_count":11,"watchers_count":11,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":11,"default_branch":"master"}},"base":{"label":"TuxedoJS:master","ref":"master","sha":"879f9f033709ab0f27b2836feb767c183c4e22c2","user":{"login":"TuxedoJS","id":10237217,"avatar_url":"https://avatars.githubusercontent.com/u/10237217?v=3","gravatar_id":"","url":"https://api.github.com/users/TuxedoJS","html_url":"https://github.com/TuxedoJS","followers_url":"https://api.github.com/users/TuxedoJS/followers","following_url":"https://api.github.com/users/TuxedoJS/following{/other_user}","gists_url":"https://api.github.com/users/TuxedoJS/gists{/gist_id}","starred_url":"https://api.github.com/users/TuxedoJS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TuxedoJS/subscriptions","organizations_url":"https://api.github.com/users/TuxedoJS/orgs","repos_url":"https://api.github.com/users/TuxedoJS/repos","events_url":"https://api.github.com/users/TuxedoJS/events{/privacy}","received_events_url":"https://api.github.com/users/TuxedoJS/received_events","type":"Organization","site_admin":false},"repo":{"id":28203450,"name":"TuxedoJS","full_name":"TuxedoJS/TuxedoJS","owner":{"login":"TuxedoJS","id":10237217,"avatar_url":"https://avatars.githubusercontent.com/u/10237217?v=3","gravatar_id":"","url":"https://api.github.com/users/TuxedoJS","html_url":"https://github.com/TuxedoJS","followers_url":"https://api.github.com/users/TuxedoJS/followers","following_url":"https://api.github.com/users/TuxedoJS/following{/other_user}","gists_url":"https://api.github.com/users/TuxedoJS/gists{/gist_id}","starred_url":"https://api.github.com/users/TuxedoJS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TuxedoJS/subscriptions","organizations_url":"https://api.github.com/users/TuxedoJS/orgs","repos_url":"https://api.github.com/users/TuxedoJS/repos","events_url":"https://api.github.com/users/TuxedoJS/events{/privacy}","received_events_url":"https://api.github.com/users/TuxedoJS/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/TuxedoJS/TuxedoJS","description":"A feature-complete framework built on React and Flux.","fork":false,"url":"https://api.github.com/repos/TuxedoJS/TuxedoJS","forks_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/forks","keys_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/teams","hooks_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/hooks","issue_events_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/events{/number}","events_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/events","assignees_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/assignees{/user}","branches_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/branches{/branch}","tags_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/tags","blobs_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/git/refs{/sha}","trees_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/statuses/{sha}","languages_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/languages","stargazers_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/stargazers","contributors_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/contributors","subscribers_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/subscribers","subscription_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/subscription","commits_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/commits{/sha}","git_commits_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/git/commits{/sha}","comments_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/comments{/number}","issue_comment_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/comments/{number}","contents_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/contents/{+path}","compare_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/merges","archive_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/downloads","issues_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues{/number}","pulls_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls{/number}","milestones_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/milestones{/number}","notifications_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/labels{/name}","releases_url":"https://api.github.com/repos/TuxedoJS/TuxedoJS/releases{/id}","created_at":"2014-12-18T21:58:57Z","updated_at":"2015-01-31T04:48:29Z","pushed_at":"2015-01-31T04:48:28Z","git_url":"git://github.com/TuxedoJS/TuxedoJS.git","ssh_url":"git@github.com:TuxedoJS/TuxedoJS.git","clone_url":"https://github.com/TuxedoJS/TuxedoJS.git","svn_url":"https://github.com/TuxedoJS/TuxedoJS","homepage":"https://tuxedojs.org","size":1780,"stargazers_count":465,"watchers_count":465,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":18,"mirror_url":null,"open_issues_count":16,"forks":18,"open_issues":16,"watchers":465,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110"},"html":{"href":"https://github.com/TuxedoJS/TuxedoJS/pull/110"},"issue":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/110"},"comments":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/issues/110/comments"},"review_comments":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110/comments"},"review_comment":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/pulls/110/commits"},"statuses":{"href":"https://api.github.com/repos/TuxedoJS/TuxedoJS/statuses/2a1f75d37f0d0c4179f0c6766b66832df101e988"}}}}