{"action":"created","comment":{"url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/comments/44162510","id":44162510,"diff_hunk":"@@ -0,0 +1,269 @@\n+#include <fstream>\n+#include <boost/range/algorithm/copy.hpp>\n+#include <boost/range/adaptors.hpp>\n+#include <boost/filesystem.hpp>\n+#include \"driveshaft-config.h\"\n+\n+namespace Driveshaft {\n+\n+namespace cfgkeys {\n+static std::string GEARMAN_SERVERS_LIST = \"gearman_servers_list\";\n+static std::string POOLS_LIST = \"pools_list\";\n+static std::string POOL_WORKER_COUNT = \"worker_count\";\n+static std::string POOL_JOB_LIST = \"jobs_list\";\n+static std::string POOL_JOB_PROCESSING_URI = \"job_processing_uri\";\n+}\n+\n+DriveshaftConfig::DriveshaftConfig() noexcept :\n+    m_config_filename(),\n+    m_server_list(),\n+    m_pool_map(),\n+    m_load_time(0) {\n+}\n+\n+bool DriveshaftConfig::load(const std::string &config_filename, Json::CharReader::Factory &parser_factory) {\n+    if (!this->needsConfigUpdate(config_filename)) {\n+        return false;\n+    }\n+\n+    LOG4CXX_INFO(MainLogger, \"Reloading config\");\n+    this->m_config_filename = config_filename;\n+\n+    std::string config_file_contents(this->fetchFileContents(config_filename));\n+    return this->parseConfig(config_file_contents, parser_factory);\n+}\n+\n+bool DriveshaftConfig::parseConfig(const std::string &config_data, Json::CharReader::Factory &parser_factory) {\n+    Json::Value tree;\n+    std::string parse_errors;\n+    auto json_parser = parser_factory.newCharReader();\n+    if (!json_parser->parse(config_data.data(),\n+                            config_data.data() + config_data.length(),\n+                            &tree, &parse_errors)) {\n+        LOG4CXX_ERROR(MainLogger, \"Failed to parse \" << this->m_config_filename <<\n+                                  \". Errors: \" << parse_errors);\n+        throw std::runtime_error(\"config parse failure\");\n+    }\n+\n+    if (!this->validateConfigNode(tree)) {\n+        throw std::runtime_error(\"config parse failure\");\n+    }\n+\n+    this->m_load_time = std::time(nullptr);\n+    this->m_server_list.clear();\n+    this->m_pool_map.clear();\n+\n+    this->parseServerList(tree);\n+    this->parsePoolList(tree);\n+\n+    return true;\n+}\n+\n+void DriveshaftConfig::supersede(DriveshaftConfig &old, PoolWatcher &watcher) const {\n+    StringSet pools_to_shut;\n+    std::tie(pools_to_shut, std::ignore) = old.compare(*this);\n+    for (const auto &pool : pools_to_shut) {\n+        old.clearWorkerCount(pool, watcher);\n+    }\n+\n+    for (auto const &i : this->m_pool_map) {\n+        const auto &pool_name = i.first;\n+        const auto &pool_data = i.second;\n+        watcher.inform(pool_data.worker_count, pool_name, this->m_server_list,\n+                       pool_data.job_list, pool_data.job_processing_uri);\n+    }\n+}\n+\n+void DriveshaftConfig::clearWorkerCount(const std::string &pool_name, PoolWatcher &watcher) {\n+    auto pool_iter = this->m_pool_map.find(pool_name);\n+    if (pool_iter == this->m_pool_map.end()) {\n+        LOG4CXX_ERROR(MainLogger, \"clearWorkerCounts requested, \" << pool_name << \" does not exist in config\");\n+        throw std::runtime_error(\"invalid config detected in clearWorkerCounts\");\n+    }\n+\n+    auto &pool_data = pool_iter->second;\n+    pool_data.worker_count = 0;\n+    watcher.inform(0, pool_name, this->m_server_list,\n+                   pool_data.job_list, pool_data.job_processing_uri);\n+}\n+\n+void DriveshaftConfig::clearAllWorkerCounts(PoolWatcher &watcher) {\n+    for (const auto &i : this->m_pool_map) {\n+        this->clearWorkerCount(i.first, watcher);\n+    }\n+}\n+\n+std::pair<StringSet, StringSet> DriveshaftConfig::compare(const DriveshaftConfig &that) const noexcept {\n+    LOG4CXX_DEBUG(MainLogger, \"Beginning config compare\");\n+\n+    StringSet current_pool_names, latest_pool_names;\n+    boost::copy(m_pool_map | boost::adaptors::map_keys,\n+                std::inserter(current_pool_names, current_pool_names.begin()));\n+    boost::copy(that.m_pool_map | boost::adaptors::map_keys,\n+                std::inserter(latest_pool_names, latest_pool_names.begin()));\n+\n+    if (this->m_server_list != that.m_server_list) {\n+        // Everything needs restarting\n+        return std::pair<StringSet, StringSet>(std::move(current_pool_names),\n+                                               std::move(latest_pool_names));\n+    } else {\n+        StringSet pools_turn_off, pools_turn_on;\n+\n+        // See what's present in current and missing in new. This is to be turned off.\n+        std::set_difference(current_pool_names.begin(), current_pool_names.end(),\n+                            latest_pool_names.begin(), latest_pool_names.end(),\n+                            std::inserter(pools_turn_off, pools_turn_off.begin()));\n+\n+        // Reverse of above. See what's to be turned on.\n+        std::set_difference(latest_pool_names.begin(), latest_pool_names.end(),\n+                            current_pool_names.begin(), current_pool_names.end(),\n+                            std::inserter(pools_turn_on, pools_turn_on.begin()));\n+\n+        // Check for changed jobs or processing URI\n+        for (const auto& i : m_pool_map) {\n+            auto found = that.m_pool_map.find(i.first);\n+            if (found != that.m_pool_map.end()) {\n+                bool should_restart = false;\n+                if (found->second.job_processing_uri != i.second.job_processing_uri) {\n+                    should_restart = true;\n+                } else {\n+                    const auto &lhs_jobs_set = i.second.job_list;\n+                    const auto &rhs_jobs_set = found->second.job_list;\n+                    StringSet diff;\n+                    std::set_symmetric_difference(lhs_jobs_set.begin(), lhs_jobs_set.end(),\n+                                                  rhs_jobs_set.begin(), rhs_jobs_set.end(),\n+                                                  std::inserter(diff, diff.begin()));\n+                    if (diff.size()) {\n+                        should_restart = true;\n+                    }\n+                }\n+\n+                if (should_restart) {\n+                    pools_turn_off.insert(i.first);\n+                    pools_turn_on.insert(i.first);\n+                }\n+            }\n+        }\n+\n+        return std::pair<StringSet, StringSet>(std::move(pools_turn_off), std::move(pools_turn_on));\n+    }\n+}\n+\n+void DriveshaftConfig::parseServerList(const Json::Value &node) {\n+    const auto &servers_list = node[cfgkeys::GEARMAN_SERVERS_LIST];\n+    for (auto i = servers_list.begin(); i != servers_list.end(); ++i) {\n+        if (!i->isString()) {\n+            LOG4CXX_ERROR(MainLogger, cfgkeys::GEARMAN_SERVERS_LIST << \" does not contain strings\");\n+            throw std::runtime_error(\"config server list type failure\");\n+        }\n+\n+        const auto &name = i->asString();\n+        this->m_server_list.insert(name);\n+        LOG4CXX_DEBUG(MainLogger, \"Read server: \" << name);\n+    }\n+}\n+\n+void DriveshaftConfig::parsePoolList(const Json::Value &node) {\n+    const auto &pools_list = node[cfgkeys::POOLS_LIST];\n+    for (auto i = pools_list.begin(); i != pools_list.end(); ++i) {\n+        const auto &pool_name = i.name();\n+        const auto &pool_node = *i;\n+\n+        if (!pool_node.isMember(cfgkeys::POOL_WORKER_COUNT) ||\n+            !pool_node.isMember(cfgkeys::POOL_JOB_LIST) ||\n+            !pool_node.isMember(cfgkeys::POOL_JOB_PROCESSING_URI) ||\n+            !pool_node[cfgkeys::POOL_WORKER_COUNT].isUInt() ||\n+            !pool_node[cfgkeys::POOL_JOB_LIST].isArray() ||\n+            !pool_node[cfgkeys::POOL_JOB_PROCESSING_URI].isString()) {\n+            LOG4CXX_ERROR(\n+                MainLogger,\n+                \"Config (\" << m_config_filename << \") has invalid \" <<\n+                cfgkeys::POOLS_LIST << \" elements: (\" <<\n+                cfgkeys::POOL_WORKER_COUNT << \", \" <<\n+                cfgkeys::POOL_JOB_PROCESSING_URI << \", \" <<\n+                cfgkeys::POOL_JOB_LIST << \")\"\n+            );\n+\n+            throw std::runtime_error(\"config jobs list parse failure\");\n+        }\n+\n+        auto &pool_data = this->m_pool_map[pool_name];\n+        pool_data.worker_count = pool_node[cfgkeys::POOL_WORKER_COUNT].asUInt();\n+        pool_data.job_processing_uri = pool_node[cfgkeys::POOL_JOB_PROCESSING_URI].asString();\n+        LOG4CXX_DEBUG(\n+            MainLogger,\n+            \"Read pool: \" << pool_name << \" with count \" <<\n+            pool_data.worker_count << \" and URI \" << pool_data.job_processing_uri\n+        );\n+\n+        const auto &job_list = pool_node[cfgkeys::POOL_JOB_LIST];\n+        for (auto j = job_list.begin(); j != job_list.end(); ++j) {\n+            if (!j->isString()) {\n+                LOG4CXX_ERROR(MainLogger, cfgkeys::POOL_JOB_LIST << \" does not contain strings\");\n+            }\n+\n+            const auto &name = j->asString();\n+            pool_data.job_list.insert(name);\n+            LOG4CXX_DEBUG(MainLogger, \"Pool \" << pool_name << \" adding job \" << name);\n+        }\n+    }\n+}\n+\n+bool DriveshaftConfig::needsConfigUpdate(const std::string &new_config_filename) const {\n+    boost::filesystem::path config_path(new_config_filename);\n+    std::time_t modified_time = boost::filesystem::last_write_time(config_path);\n+    if (modified_time == (std::time_t)(-1)) {\n+        LOG4CXX_ERROR(\n+            MainLogger,\n+            \"Unable to ascertain config mtime \" << new_config_filename <<","path":"src/driveshaft-config.cpp","position":218,"original_position":218,"commit_id":"2acb7b4ce8acf73150d9890473cb53b15f98d6c0","original_commit_id":"2acb7b4ce8acf73150d9890473cb53b15f98d6c0","user":{"login":"kyleds","id":14931595,"avatar_url":"https://avatars.githubusercontent.com/u/14931595?v=3","gravatar_id":"","url":"https://api.github.com/users/kyleds","html_url":"https://github.com/kyleds","followers_url":"https://api.github.com/users/kyleds/followers","following_url":"https://api.github.com/users/kyleds/following{/other_user}","gists_url":"https://api.github.com/users/kyleds/gists{/gist_id}","starred_url":"https://api.github.com/users/kyleds/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kyleds/subscriptions","organizations_url":"https://api.github.com/users/kyleds/orgs","repos_url":"https://api.github.com/users/kyleds/repos","events_url":"https://api.github.com/users/kyleds/events{/privacy}","received_events_url":"https://api.github.com/users/kyleds/received_events","type":"User","site_admin":false},"body":"@mrtazz sure :)\r\n\r\n[Here](http://www.boost.org/doc/libs/1_32_0/libs/filesystem/doc/operations.htm#last_write_time) was that documentation, btw.","created_at":"2015-11-06T17:20:14Z","updated_at":"2015-11-06T17:20:14Z","html_url":"https://github.com/keyurdg/driveshaft/pull/14#discussion_r44162510","pull_request_url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14","_links":{"self":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/comments/44162510"},"html":{"href":"https://github.com/keyurdg/driveshaft/pull/14#discussion_r44162510"},"pull_request":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14"}}},"pull_request":{"url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14","id":47052796,"html_url":"https://github.com/keyurdg/driveshaft/pull/14","diff_url":"https://github.com/keyurdg/driveshaft/pull/14.diff","patch_url":"https://github.com/keyurdg/driveshaft/pull/14.patch","issue_url":"https://api.github.com/repos/keyurdg/driveshaft/issues/14","number":14,"state":"open","locked":false,"title":"Gearman client tests","user":{"login":"kyleds","id":14931595,"avatar_url":"https://avatars.githubusercontent.com/u/14931595?v=3","gravatar_id":"","url":"https://api.github.com/users/kyleds","html_url":"https://github.com/kyleds","followers_url":"https://api.github.com/users/kyleds/followers","following_url":"https://api.github.com/users/kyleds/following{/other_user}","gists_url":"https://api.github.com/users/kyleds/gists{/gist_id}","starred_url":"https://api.github.com/users/kyleds/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kyleds/subscriptions","organizations_url":"https://api.github.com/users/kyleds/orgs","repos_url":"https://api.github.com/users/kyleds/repos","events_url":"https://api.github.com/users/kyleds/events{/privacy}","received_events_url":"https://api.github.com/users/kyleds/received_events","type":"User","site_admin":false},"body":"Similar to the prior PR, but with fewer refactorings necessary in src/. \r\n\r\nNote that I branched from my prior work so this PR includes those commits as well. I think it should be fine to merge either?","created_at":"2015-10-07T16:51:23Z","updated_at":"2015-11-06T17:20:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"18f5dfc5cbff9ebd316445aa8b0e822823a3894d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14/commits","review_comments_url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14/comments","review_comment_url":"https://api.github.com/repos/keyurdg/driveshaft/pulls/comments{/number}","comments_url":"https://api.github.com/repos/keyurdg/driveshaft/issues/14/comments","statuses_url":"https://api.github.com/repos/keyurdg/driveshaft/statuses/2acb7b4ce8acf73150d9890473cb53b15f98d6c0","head":{"label":"kyleds:gearman_client_tests","ref":"gearman_client_tests","sha":"2acb7b4ce8acf73150d9890473cb53b15f98d6c0","user":{"login":"kyleds","id":14931595,"avatar_url":"https://avatars.githubusercontent.com/u/14931595?v=3","gravatar_id":"","url":"https://api.github.com/users/kyleds","html_url":"https://github.com/kyleds","followers_url":"https://api.github.com/users/kyleds/followers","following_url":"https://api.github.com/users/kyleds/following{/other_user}","gists_url":"https://api.github.com/users/kyleds/gists{/gist_id}","starred_url":"https://api.github.com/users/kyleds/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kyleds/subscriptions","organizations_url":"https://api.github.com/users/kyleds/orgs","repos_url":"https://api.github.com/users/kyleds/repos","events_url":"https://api.github.com/users/kyleds/events{/privacy}","received_events_url":"https://api.github.com/users/kyleds/received_events","type":"User","site_admin":false},"repo":{"id":43520899,"name":"driveshaft","full_name":"kyleds/driveshaft","owner":{"login":"kyleds","id":14931595,"avatar_url":"https://avatars.githubusercontent.com/u/14931595?v=3","gravatar_id":"","url":"https://api.github.com/users/kyleds","html_url":"https://github.com/kyleds","followers_url":"https://api.github.com/users/kyleds/followers","following_url":"https://api.github.com/users/kyleds/following{/other_user}","gists_url":"https://api.github.com/users/kyleds/gists{/gist_id}","starred_url":"https://api.github.com/users/kyleds/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kyleds/subscriptions","organizations_url":"https://api.github.com/users/kyleds/orgs","repos_url":"https://api.github.com/users/kyleds/repos","events_url":"https://api.github.com/users/kyleds/events{/privacy}","received_events_url":"https://api.github.com/users/kyleds/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/kyleds/driveshaft","description":"Gearman worker manager","fork":true,"url":"https://api.github.com/repos/kyleds/driveshaft","forks_url":"https://api.github.com/repos/kyleds/driveshaft/forks","keys_url":"https://api.github.com/repos/kyleds/driveshaft/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kyleds/driveshaft/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kyleds/driveshaft/teams","hooks_url":"https://api.github.com/repos/kyleds/driveshaft/hooks","issue_events_url":"https://api.github.com/repos/kyleds/driveshaft/issues/events{/number}","events_url":"https://api.github.com/repos/kyleds/driveshaft/events","assignees_url":"https://api.github.com/repos/kyleds/driveshaft/assignees{/user}","branches_url":"https://api.github.com/repos/kyleds/driveshaft/branches{/branch}","tags_url":"https://api.github.com/repos/kyleds/driveshaft/tags","blobs_url":"https://api.github.com/repos/kyleds/driveshaft/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kyleds/driveshaft/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kyleds/driveshaft/git/refs{/sha}","trees_url":"https://api.github.com/repos/kyleds/driveshaft/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kyleds/driveshaft/statuses/{sha}","languages_url":"https://api.github.com/repos/kyleds/driveshaft/languages","stargazers_url":"https://api.github.com/repos/kyleds/driveshaft/stargazers","contributors_url":"https://api.github.com/repos/kyleds/driveshaft/contributors","subscribers_url":"https://api.github.com/repos/kyleds/driveshaft/subscribers","subscription_url":"https://api.github.com/repos/kyleds/driveshaft/subscription","commits_url":"https://api.github.com/repos/kyleds/driveshaft/commits{/sha}","git_commits_url":"https://api.github.com/repos/kyleds/driveshaft/git/commits{/sha}","comments_url":"https://api.github.com/repos/kyleds/driveshaft/comments{/number}","issue_comment_url":"https://api.github.com/repos/kyleds/driveshaft/issues/comments{/number}","contents_url":"https://api.github.com/repos/kyleds/driveshaft/contents/{+path}","compare_url":"https://api.github.com/repos/kyleds/driveshaft/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kyleds/driveshaft/merges","archive_url":"https://api.github.com/repos/kyleds/driveshaft/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kyleds/driveshaft/downloads","issues_url":"https://api.github.com/repos/kyleds/driveshaft/issues{/number}","pulls_url":"https://api.github.com/repos/kyleds/driveshaft/pulls{/number}","milestones_url":"https://api.github.com/repos/kyleds/driveshaft/milestones{/number}","notifications_url":"https://api.github.com/repos/kyleds/driveshaft/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kyleds/driveshaft/labels{/name}","releases_url":"https://api.github.com/repos/kyleds/driveshaft/releases{/id}","created_at":"2015-10-01T21:00:56Z","updated_at":"2015-10-01T21:00:58Z","pushed_at":"2015-11-06T17:19:38Z","git_url":"git://github.com/kyleds/driveshaft.git","ssh_url":"git@github.com:kyleds/driveshaft.git","clone_url":"https://github.com/kyleds/driveshaft.git","svn_url":"https://github.com/kyleds/driveshaft","homepage":null,"size":1164,"stargazers_count":0,"watchers_count":0,"language":"C++","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":"keyurdg:master","ref":"master","sha":"7a3ebcd51b46a5da126b67ededc1105ca45b61d3","user":{"login":"keyurdg","id":2280740,"avatar_url":"https://avatars.githubusercontent.com/u/2280740?v=3","gravatar_id":"","url":"https://api.github.com/users/keyurdg","html_url":"https://github.com/keyurdg","followers_url":"https://api.github.com/users/keyurdg/followers","following_url":"https://api.github.com/users/keyurdg/following{/other_user}","gists_url":"https://api.github.com/users/keyurdg/gists{/gist_id}","starred_url":"https://api.github.com/users/keyurdg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/keyurdg/subscriptions","organizations_url":"https://api.github.com/users/keyurdg/orgs","repos_url":"https://api.github.com/users/keyurdg/repos","events_url":"https://api.github.com/users/keyurdg/events{/privacy}","received_events_url":"https://api.github.com/users/keyurdg/received_events","type":"User","site_admin":false},"repo":{"id":42800095,"name":"driveshaft","full_name":"keyurdg/driveshaft","owner":{"login":"keyurdg","id":2280740,"avatar_url":"https://avatars.githubusercontent.com/u/2280740?v=3","gravatar_id":"","url":"https://api.github.com/users/keyurdg","html_url":"https://github.com/keyurdg","followers_url":"https://api.github.com/users/keyurdg/followers","following_url":"https://api.github.com/users/keyurdg/following{/other_user}","gists_url":"https://api.github.com/users/keyurdg/gists{/gist_id}","starred_url":"https://api.github.com/users/keyurdg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/keyurdg/subscriptions","organizations_url":"https://api.github.com/users/keyurdg/orgs","repos_url":"https://api.github.com/users/keyurdg/repos","events_url":"https://api.github.com/users/keyurdg/events{/privacy}","received_events_url":"https://api.github.com/users/keyurdg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/keyurdg/driveshaft","description":"Gearman worker manager","fork":false,"url":"https://api.github.com/repos/keyurdg/driveshaft","forks_url":"https://api.github.com/repos/keyurdg/driveshaft/forks","keys_url":"https://api.github.com/repos/keyurdg/driveshaft/keys{/key_id}","collaborators_url":"https://api.github.com/repos/keyurdg/driveshaft/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/keyurdg/driveshaft/teams","hooks_url":"https://api.github.com/repos/keyurdg/driveshaft/hooks","issue_events_url":"https://api.github.com/repos/keyurdg/driveshaft/issues/events{/number}","events_url":"https://api.github.com/repos/keyurdg/driveshaft/events","assignees_url":"https://api.github.com/repos/keyurdg/driveshaft/assignees{/user}","branches_url":"https://api.github.com/repos/keyurdg/driveshaft/branches{/branch}","tags_url":"https://api.github.com/repos/keyurdg/driveshaft/tags","blobs_url":"https://api.github.com/repos/keyurdg/driveshaft/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/keyurdg/driveshaft/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/keyurdg/driveshaft/git/refs{/sha}","trees_url":"https://api.github.com/repos/keyurdg/driveshaft/git/trees{/sha}","statuses_url":"https://api.github.com/repos/keyurdg/driveshaft/statuses/{sha}","languages_url":"https://api.github.com/repos/keyurdg/driveshaft/languages","stargazers_url":"https://api.github.com/repos/keyurdg/driveshaft/stargazers","contributors_url":"https://api.github.com/repos/keyurdg/driveshaft/contributors","subscribers_url":"https://api.github.com/repos/keyurdg/driveshaft/subscribers","subscription_url":"https://api.github.com/repos/keyurdg/driveshaft/subscription","commits_url":"https://api.github.com/repos/keyurdg/driveshaft/commits{/sha}","git_commits_url":"https://api.github.com/repos/keyurdg/driveshaft/git/commits{/sha}","comments_url":"https://api.github.com/repos/keyurdg/driveshaft/comments{/number}","issue_comment_url":"https://api.github.com/repos/keyurdg/driveshaft/issues/comments{/number}","contents_url":"https://api.github.com/repos/keyurdg/driveshaft/contents/{+path}","compare_url":"https://api.github.com/repos/keyurdg/driveshaft/compare/{base}...{head}","merges_url":"https://api.github.com/repos/keyurdg/driveshaft/merges","archive_url":"https://api.github.com/repos/keyurdg/driveshaft/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/keyurdg/driveshaft/downloads","issues_url":"https://api.github.com/repos/keyurdg/driveshaft/issues{/number}","pulls_url":"https://api.github.com/repos/keyurdg/driveshaft/pulls{/number}","milestones_url":"https://api.github.com/repos/keyurdg/driveshaft/milestones{/number}","notifications_url":"https://api.github.com/repos/keyurdg/driveshaft/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/keyurdg/driveshaft/labels{/name}","releases_url":"https://api.github.com/repos/keyurdg/driveshaft/releases{/id}","created_at":"2015-09-20T04:27:49Z","updated_at":"2015-10-27T16:20:19Z","pushed_at":"2015-11-06T17:19:40Z","git_url":"git://github.com/keyurdg/driveshaft.git","ssh_url":"git@github.com:keyurdg/driveshaft.git","clone_url":"https://github.com/keyurdg/driveshaft.git","svn_url":"https://github.com/keyurdg/driveshaft","homepage":null,"size":1435,"stargazers_count":23,"watchers_count":23,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":5,"forks":6,"open_issues":5,"watchers":23,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14"},"html":{"href":"https://github.com/keyurdg/driveshaft/pull/14"},"issue":{"href":"https://api.github.com/repos/keyurdg/driveshaft/issues/14"},"comments":{"href":"https://api.github.com/repos/keyurdg/driveshaft/issues/14/comments"},"review_comments":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14/comments"},"review_comment":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/keyurdg/driveshaft/pulls/14/commits"},"statuses":{"href":"https://api.github.com/repos/keyurdg/driveshaft/statuses/2acb7b4ce8acf73150d9890473cb53b15f98d6c0"}}}}