{"action":"created","comment":{"url":"https://api.github.com/repos/deconst/presenter/pulls/comments/50705153","id":50705153,"diff_hunk":"@@ -219,30 +220,51 @@ var ControlService = {\n \n module.exports = ControlService;\n \n-var maybeParseJSON = function (filename, def, callback) {\n-  fs.readFile(filename, {encoding: 'utf-8'}, function (err, body) {\n-    if (err) {\n-      if (err.code === 'ENOENT') {\n-        return callback(null, def);\n+var readAndMergeConfigFiles = function (files, def, callback) {\n+  // for compatibility. Might be called with files as a single path, not an\n+  // array of paths\n+  if (!Array.isArray(files)) {\n+    files = [files];\n+  }\n+\n+  async.reduce(files, {}, function (previousValue, currentValue, reduceCallback) {\n+    fs.readFile(currentValue, {encoding: 'utf-8'}, function (err, body) {\n+      if (err) {\n+        if (err.code === 'ENOENT') {\n+          return callback(null, def);\n+        }\n+\n+        return callback(err);\n       }\n \n-      return callback(err);\n-    }\n+      var doc;\n+      try {\n+        doc = JSON.parse(body);\n+      } catch (e) {\n+        doc = {};\n+        logger.warn('Configuration file contained invalid JSON', {\n+          errMessage: e.message,\n+          filename: currentValue,\n+          source: body\n+        });\n+      }\n \n-    var doc;\n-    try {\n-      doc = JSON.parse(body);\n-    } catch (e) {\n-      logger.warn('Configuration file contained invalid JSON', {\n-        errMessage: e.message,\n-        filename: filename,\n-        source: body\n-      });\n+      // I'm surprised this little concatenation loop works as well as it does.\n+      // Could definitely use some testing to be sure it covers all the\n+      // _reasonable_ use cases.\n+      for (var site in doc) {\n+        if (doc.hasOwnProperty(site)) {\n+          if (previousValue.hasOwnProperty(site)) {\n+            previousValue[site] = previousValue[site].concat(doc[site]);\n+          } else {\n+            previousValue[site] = doc[site];\n+          }\n+        }\n+      }","path":"src/services/control.js","position":68,"original_position":68,"commit_id":"8b8fdac0bc35df6cc94abc7d8bb1b09548304a26","original_commit_id":"8b8fdac0bc35df6cc94abc7d8bb1b09548304a26","user":{"login":"ktbartholomew","id":3018940,"avatar_url":"https://avatars.githubusercontent.com/u/3018940?v=3","gravatar_id":"","url":"https://api.github.com/users/ktbartholomew","html_url":"https://github.com/ktbartholomew","followers_url":"https://api.github.com/users/ktbartholomew/followers","following_url":"https://api.github.com/users/ktbartholomew/following{/other_user}","gists_url":"https://api.github.com/users/ktbartholomew/gists{/gist_id}","starred_url":"https://api.github.com/users/ktbartholomew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktbartholomew/subscriptions","organizations_url":"https://api.github.com/users/ktbartholomew/orgs","repos_url":"https://api.github.com/users/ktbartholomew/repos","events_url":"https://api.github.com/users/ktbartholomew/events{/privacy}","received_events_url":"https://api.github.com/users/ktbartholomew/received_events","type":"User","site_admin":false},"body":"I'll revisit this when the control repo gets its proper amount of attention in https://github.com/rackerlabs/nexus-control/issues/271","created_at":"2016-01-25T15:13:53Z","updated_at":"2016-01-25T15:13:53Z","html_url":"https://github.com/deconst/presenter/pull/90#discussion_r50705153","pull_request_url":"https://api.github.com/repos/deconst/presenter/pulls/90","_links":{"self":{"href":"https://api.github.com/repos/deconst/presenter/pulls/comments/50705153"},"html":{"href":"https://github.com/deconst/presenter/pull/90#discussion_r50705153"},"pull_request":{"href":"https://api.github.com/repos/deconst/presenter/pulls/90"}}},"pull_request":{"url":"https://api.github.com/repos/deconst/presenter/pulls/90","id":56931977,"html_url":"https://github.com/deconst/presenter/pull/90","diff_url":"https://github.com/deconst/presenter/pull/90.diff","patch_url":"https://github.com/deconst/presenter/pull/90.patch","issue_url":"https://api.github.com/repos/deconst/presenter/issues/90","number":90,"state":"open","locked":false,"title":"modify JSON parsing function to read, parse, and merge many files","user":{"login":"ktbartholomew","id":3018940,"avatar_url":"https://avatars.githubusercontent.com/u/3018940?v=3","gravatar_id":"","url":"https://api.github.com/users/ktbartholomew","html_url":"https://github.com/ktbartholomew","followers_url":"https://api.github.com/users/ktbartholomew/followers","following_url":"https://api.github.com/users/ktbartholomew/following{/other_user}","gists_url":"https://api.github.com/users/ktbartholomew/gists{/gist_id}","starred_url":"https://api.github.com/users/ktbartholomew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktbartholomew/subscriptions","organizations_url":"https://api.github.com/users/ktbartholomew/orgs","repos_url":"https://api.github.com/users/ktbartholomew/repos","events_url":"https://api.github.com/users/ktbartholomew/events{/privacy}","received_events_url":"https://api.github.com/users/ktbartholomew/received_events","type":"User","site_admin":false},"body":"This addresses the desire to support multiple config files as described in https://github.com/deconst/presenter/issues/89.\r\n\r\nI'm using a little glob to support multiple config files without explicitly requiring them:\r\n\r\n```javascript\r\nvar rewriteFiles = globby.sync([\r\n    PathService.getConfigPath(config.control_rewrites_file()),\r\n    PathService.getConfigPath('rewrites.d/**/*')\r\n]);\r\n```\r\n\r\nThen, I modified the `maybeParseJSON` helper function to reduce all of the config files into one big config object, allowing all the other internals to stay exactly the way they are right now.","created_at":"2016-01-22T19:40:08Z","updated_at":"2016-01-25T15:13:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c5f36f3f93d1e94d942984be0ba96cd4b4ce83ed","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/deconst/presenter/pulls/90/commits","review_comments_url":"https://api.github.com/repos/deconst/presenter/pulls/90/comments","review_comment_url":"https://api.github.com/repos/deconst/presenter/pulls/comments{/number}","comments_url":"https://api.github.com/repos/deconst/presenter/issues/90/comments","statuses_url":"https://api.github.com/repos/deconst/presenter/statuses/8b8fdac0bc35df6cc94abc7d8bb1b09548304a26","head":{"label":"ktbartholomew:dot-d-config-folders","ref":"dot-d-config-folders","sha":"8b8fdac0bc35df6cc94abc7d8bb1b09548304a26","user":{"login":"ktbartholomew","id":3018940,"avatar_url":"https://avatars.githubusercontent.com/u/3018940?v=3","gravatar_id":"","url":"https://api.github.com/users/ktbartholomew","html_url":"https://github.com/ktbartholomew","followers_url":"https://api.github.com/users/ktbartholomew/followers","following_url":"https://api.github.com/users/ktbartholomew/following{/other_user}","gists_url":"https://api.github.com/users/ktbartholomew/gists{/gist_id}","starred_url":"https://api.github.com/users/ktbartholomew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktbartholomew/subscriptions","organizations_url":"https://api.github.com/users/ktbartholomew/orgs","repos_url":"https://api.github.com/users/ktbartholomew/repos","events_url":"https://api.github.com/users/ktbartholomew/events{/privacy}","received_events_url":"https://api.github.com/users/ktbartholomew/received_events","type":"User","site_admin":false},"repo":{"id":37493376,"name":"presenter","full_name":"ktbartholomew/presenter","owner":{"login":"ktbartholomew","id":3018940,"avatar_url":"https://avatars.githubusercontent.com/u/3018940?v=3","gravatar_id":"","url":"https://api.github.com/users/ktbartholomew","html_url":"https://github.com/ktbartholomew","followers_url":"https://api.github.com/users/ktbartholomew/followers","following_url":"https://api.github.com/users/ktbartholomew/following{/other_user}","gists_url":"https://api.github.com/users/ktbartholomew/gists{/gist_id}","starred_url":"https://api.github.com/users/ktbartholomew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktbartholomew/subscriptions","organizations_url":"https://api.github.com/users/ktbartholomew/orgs","repos_url":"https://api.github.com/users/ktbartholomew/repos","events_url":"https://api.github.com/users/ktbartholomew/events{/privacy}","received_events_url":"https://api.github.com/users/ktbartholomew/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ktbartholomew/presenter","description":"A component that builds and returns the final document.","fork":true,"url":"https://api.github.com/repos/ktbartholomew/presenter","forks_url":"https://api.github.com/repos/ktbartholomew/presenter/forks","keys_url":"https://api.github.com/repos/ktbartholomew/presenter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ktbartholomew/presenter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ktbartholomew/presenter/teams","hooks_url":"https://api.github.com/repos/ktbartholomew/presenter/hooks","issue_events_url":"https://api.github.com/repos/ktbartholomew/presenter/issues/events{/number}","events_url":"https://api.github.com/repos/ktbartholomew/presenter/events","assignees_url":"https://api.github.com/repos/ktbartholomew/presenter/assignees{/user}","branches_url":"https://api.github.com/repos/ktbartholomew/presenter/branches{/branch}","tags_url":"https://api.github.com/repos/ktbartholomew/presenter/tags","blobs_url":"https://api.github.com/repos/ktbartholomew/presenter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ktbartholomew/presenter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ktbartholomew/presenter/git/refs{/sha}","trees_url":"https://api.github.com/repos/ktbartholomew/presenter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ktbartholomew/presenter/statuses/{sha}","languages_url":"https://api.github.com/repos/ktbartholomew/presenter/languages","stargazers_url":"https://api.github.com/repos/ktbartholomew/presenter/stargazers","contributors_url":"https://api.github.com/repos/ktbartholomew/presenter/contributors","subscribers_url":"https://api.github.com/repos/ktbartholomew/presenter/subscribers","subscription_url":"https://api.github.com/repos/ktbartholomew/presenter/subscription","commits_url":"https://api.github.com/repos/ktbartholomew/presenter/commits{/sha}","git_commits_url":"https://api.github.com/repos/ktbartholomew/presenter/git/commits{/sha}","comments_url":"https://api.github.com/repos/ktbartholomew/presenter/comments{/number}","issue_comment_url":"https://api.github.com/repos/ktbartholomew/presenter/issues/comments{/number}","contents_url":"https://api.github.com/repos/ktbartholomew/presenter/contents/{+path}","compare_url":"https://api.github.com/repos/ktbartholomew/presenter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ktbartholomew/presenter/merges","archive_url":"https://api.github.com/repos/ktbartholomew/presenter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ktbartholomew/presenter/downloads","issues_url":"https://api.github.com/repos/ktbartholomew/presenter/issues{/number}","pulls_url":"https://api.github.com/repos/ktbartholomew/presenter/pulls{/number}","milestones_url":"https://api.github.com/repos/ktbartholomew/presenter/milestones{/number}","notifications_url":"https://api.github.com/repos/ktbartholomew/presenter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ktbartholomew/presenter/labels{/name}","releases_url":"https://api.github.com/repos/ktbartholomew/presenter/releases{/id}","deployments_url":"https://api.github.com/repos/ktbartholomew/presenter/deployments","created_at":"2015-06-15T21:52:16Z","updated_at":"2015-06-15T21:52:16Z","pushed_at":"2016-01-22T19:41:36Z","git_url":"git://github.com/ktbartholomew/presenter.git","ssh_url":"git@github.com:ktbartholomew/presenter.git","clone_url":"https://github.com/ktbartholomew/presenter.git","svn_url":"https://github.com/ktbartholomew/presenter","homepage":null,"size":278,"stargazers_count":0,"watchers_count":0,"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":0,"default_branch":"master"}},"base":{"label":"deconst:master","ref":"master","sha":"b8460afdca23367b0d9266db4489ac9dd74e81d5","user":{"login":"deconst","id":11205298,"avatar_url":"https://avatars.githubusercontent.com/u/11205298?v=3","gravatar_id":"","url":"https://api.github.com/users/deconst","html_url":"https://github.com/deconst","followers_url":"https://api.github.com/users/deconst/followers","following_url":"https://api.github.com/users/deconst/following{/other_user}","gists_url":"https://api.github.com/users/deconst/gists{/gist_id}","starred_url":"https://api.github.com/users/deconst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deconst/subscriptions","organizations_url":"https://api.github.com/users/deconst/orgs","repos_url":"https://api.github.com/users/deconst/repos","events_url":"https://api.github.com/users/deconst/events{/privacy}","received_events_url":"https://api.github.com/users/deconst/received_events","type":"Organization","site_admin":false},"repo":{"id":32496221,"name":"presenter","full_name":"deconst/presenter","owner":{"login":"deconst","id":11205298,"avatar_url":"https://avatars.githubusercontent.com/u/11205298?v=3","gravatar_id":"","url":"https://api.github.com/users/deconst","html_url":"https://github.com/deconst","followers_url":"https://api.github.com/users/deconst/followers","following_url":"https://api.github.com/users/deconst/following{/other_user}","gists_url":"https://api.github.com/users/deconst/gists{/gist_id}","starred_url":"https://api.github.com/users/deconst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deconst/subscriptions","organizations_url":"https://api.github.com/users/deconst/orgs","repos_url":"https://api.github.com/users/deconst/repos","events_url":"https://api.github.com/users/deconst/events{/privacy}","received_events_url":"https://api.github.com/users/deconst/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/deconst/presenter","description":"A component that builds and returns the final document.","fork":false,"url":"https://api.github.com/repos/deconst/presenter","forks_url":"https://api.github.com/repos/deconst/presenter/forks","keys_url":"https://api.github.com/repos/deconst/presenter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/deconst/presenter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/deconst/presenter/teams","hooks_url":"https://api.github.com/repos/deconst/presenter/hooks","issue_events_url":"https://api.github.com/repos/deconst/presenter/issues/events{/number}","events_url":"https://api.github.com/repos/deconst/presenter/events","assignees_url":"https://api.github.com/repos/deconst/presenter/assignees{/user}","branches_url":"https://api.github.com/repos/deconst/presenter/branches{/branch}","tags_url":"https://api.github.com/repos/deconst/presenter/tags","blobs_url":"https://api.github.com/repos/deconst/presenter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/deconst/presenter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/deconst/presenter/git/refs{/sha}","trees_url":"https://api.github.com/repos/deconst/presenter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/deconst/presenter/statuses/{sha}","languages_url":"https://api.github.com/repos/deconst/presenter/languages","stargazers_url":"https://api.github.com/repos/deconst/presenter/stargazers","contributors_url":"https://api.github.com/repos/deconst/presenter/contributors","subscribers_url":"https://api.github.com/repos/deconst/presenter/subscribers","subscription_url":"https://api.github.com/repos/deconst/presenter/subscription","commits_url":"https://api.github.com/repos/deconst/presenter/commits{/sha}","git_commits_url":"https://api.github.com/repos/deconst/presenter/git/commits{/sha}","comments_url":"https://api.github.com/repos/deconst/presenter/comments{/number}","issue_comment_url":"https://api.github.com/repos/deconst/presenter/issues/comments{/number}","contents_url":"https://api.github.com/repos/deconst/presenter/contents/{+path}","compare_url":"https://api.github.com/repos/deconst/presenter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/deconst/presenter/merges","archive_url":"https://api.github.com/repos/deconst/presenter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/deconst/presenter/downloads","issues_url":"https://api.github.com/repos/deconst/presenter/issues{/number}","pulls_url":"https://api.github.com/repos/deconst/presenter/pulls{/number}","milestones_url":"https://api.github.com/repos/deconst/presenter/milestones{/number}","notifications_url":"https://api.github.com/repos/deconst/presenter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/deconst/presenter/labels{/name}","releases_url":"https://api.github.com/repos/deconst/presenter/releases{/id}","deployments_url":"https://api.github.com/repos/deconst/presenter/deployments","created_at":"2015-03-19T02:28:24Z","updated_at":"2016-01-07T16:48:18Z","pushed_at":"2016-01-22T19:41:36Z","git_url":"git://github.com/deconst/presenter.git","ssh_url":"git@github.com:deconst/presenter.git","clone_url":"https://github.com/deconst/presenter.git","svn_url":"https://github.com/deconst/presenter","homepage":null,"size":290,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":3,"forks":2,"open_issues":3,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/deconst/presenter/pulls/90"},"html":{"href":"https://github.com/deconst/presenter/pull/90"},"issue":{"href":"https://api.github.com/repos/deconst/presenter/issues/90"},"comments":{"href":"https://api.github.com/repos/deconst/presenter/issues/90/comments"},"review_comments":{"href":"https://api.github.com/repos/deconst/presenter/pulls/90/comments"},"review_comment":{"href":"https://api.github.com/repos/deconst/presenter/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/deconst/presenter/pulls/90/commits"},"statuses":{"href":"https://api.github.com/repos/deconst/presenter/statuses/8b8fdac0bc35df6cc94abc7d8bb1b09548304a26"}}}}