{"action":"created","comment":{"url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/comments/27092974","id":27092974,"diff_hunk":"@@ -244,26 +138,145 @@ class Metasploit::Cache::Module::Ancestor < ActiveRecord::Base\n   # Class Methods\n   #\n \n-  # Returns whether {#handler_type} is required or must be `nil` for the given payload_type.\n+  # @note The yielded {Metasploit::Cache::Module::Ancestor} may contain unsaved changes.  It is the responsibility of\n+  #   the caller to save the record.\n   #\n-  # @param options [Hash{Symbol => String,nil}]\n-  # @option options [String, nil] module_type (nil) `nil` or an element of\n-  #   `Metasploit::Cache::Module::Ancestor::MODULE_TYPES`.\n-  # @option options [String, nil] payload_type (nil) `nil` or an element of {PAYLOAD_TYPES}.\n-  # @return [true] if {#handler_type} must be present.\n-  # @return [false] if {#handler_type} must be `nil`.\n-  def self.handled?(options={})\n-    options.assert_valid_keys(:module_type, :payload_type)\n+  # @overload each_changed(assume_changed: false, progress_bar: Metasploit::Cache::NullProgressBar.new, relative_paths:, scope:)\n+  #   Yields each module ancestor that is changed on `relative_paths`.\n+  #\n+  #   @yield [module_ancestor]\n+  #   @yieldparam module_ancestor [Metasploit::Cache::Module::Ancestor] a changed, or in the case `assume_changed` is\n+  #     `true`, assumed changed, {Metasploit::Cache::Module::Ancestor}.\n+  #   @yieldreturn [void]\n+  #   @return [void]\n+  #\n+  # @overload each_changed(assume_changed: false, progress_bar: Metasploit::Cache::NullProgressBar.new, relative_paths:, scope:)\n+  #   Returns enumerator that yields each module ancestor that is changed under `relative_paths`.\n+  #\n+  #   @return [Enumerator<Metasploit::Cache::Module::Ancestor>]\n+  #\n+  # @param assume_changed [Boolean] if `true`, assume the {Metasploit::Cache::Module::Ancestor#real_path_modified_at}\n+  #   and {Metasploit::Cache::Module::Ancestor#real_path_sha1_hex_digest} have changed and that\n+  #   {Metasploit::Cache::Module::Ancestor} should be yielded.\n+  # @param progress_bar [ProgressBar, #total=, #increment] a ruby `ProgressBar` or similar object that supports the\n+  #   `#total=` and `#increment` API for monitoring the progress of the enumerator.  `#total` will be set to total\n+  #   number of `relative_paths`, not just the number of changed (updated or new) relative_paths.  `#increment` will be\n+  #   called whenever a relative path is visited, which means it can be called when there is no yielded module ancestor\n+  #   because that module ancestor was unchanged.  When {each_changed} returns, `#increment` will have been called the\n+  #   same number of times as the value passed to `#total=` and `#finished?` will be `true`.\n+  # @param relative_paths [Array<String>] an `Array` of {Metasploit::Cache::Module::Ancestor#real_path}.\n+  # @param scope [ActiveRecord::Relation<Class<Metasploit::Cache::Module::Ancestor>>] scope for a\n+  #   `Class<Metasploit::Cache::Module::Ancestor>`.\n+  def self.each_changed(assume_changed: false, progress_bar: Metasploit::Cache::NullProgressBar.new, relative_paths:, scope:)\n+    if block_given?\n+      progress_bar.total = relative_paths.length\n+\n+      ActiveRecord::Base.connection_pool.with_connection do\n+        updatable_module_ancestors = scope.where(relative_path: relative_paths)\n+        new_relative_path_set = Set.new relative_paths\n+\n+        # use find_each since this is expected to exceed default batch size of 1000 records.\n+        updatable_module_ancestors.find_each do |updatable_module_ancestor|\n+          new_relative_path_set.delete(updatable_module_ancestor.relative_path)\n+\n+          changed = assume_changed\n+\n+          # real_path_modified_at and real_path_sha1_hex_digest should be updated even if assume_changed is true so\n+          # that database stays in-sync with file system\n+\n+          updatable_module_ancestor.real_path_modified_at = updatable_module_ancestor.derived_real_path_modified_at\n+\n+          # only derive the SHA1 Hex Digest if modification time has changed to save time\n+          if updatable_module_ancestor.real_path_modified_at_changed?\n+            updatable_module_ancestor.real_path_sha1_hex_digest = updatable_module_ancestor.derived_real_path_sha1_hex_digest\n+\n+            changed ||= updatable_module_ancestor.real_path_sha1_hex_digest_changed?\n+          end\n+\n+          if changed\n+            yield updatable_module_ancestor\n+            progress_bar.increment\n+          else\n+            # increment even when no yield so that increment occurs for each path and matches totally without jumps\n+            progress_bar.increment\n+          end\n+        end\n+\n+        # after all pre-existing relative_paths are subtracted, new_relative_path_set contains only relative_paths not\n+        # in the database\n+        new_relative_path_set.each do |relative_path|\n+          new_module_ancestor = scope.new(relative_path: relative_path)\n+\n+          yield new_module_ancestor\n+          progress_bar.increment\n+        end\n+      end\n+    else\n+      enum_for(\n+          __method__,\n+          assume_changed: assume_changed,\n+          progress_bar: progress_bar,\n+          relative_paths: relative_paths,\n+          scope: scope\n+      )\n+    end\n+  end\n+\n+  # Ensure that only {#module_type} matching `MODULE_TYPE` is valid for `subclass`.\n+  #\n+  # @param subclass [Class<Metasploit::Cache::Module::Ancestor>] a subclass of {Metasploit::Cache::Module::Ancestor}.\n+  # @return [void]\n+  def self.restrict(subclass)\n+    #\n+    # Validations\n+    #\n \n-    handled = false\n-    module_type = options[:module_type]\n-    payload_type = options[:payload_type]\n+    subclass.validate :module_type_matches\n \n-    if module_type == 'payload' and HANDLED_TYPES.include? payload_type\n-      handled = true\n+    #\n+    # Class Methods\n+    #\n+\n+    subclass_relative_path_prefix = \"#{subclass::MODULE_TYPE_DIRECTORY}\".freeze\n+\n+    subclass.define_singleton_method(:relative_path_prefix) do","path":"app/models/metasploit/cache/module/ancestor.rb","position":359,"original_position":359,"commit_id":"4da0516e4abef8cb8e04e823a042b4f7c3813c24","original_commit_id":"4da0516e4abef8cb8e04e823a042b4f7c3813c24","user":{"login":"jlee-r7","id":1173675,"avatar_url":"https://avatars.githubusercontent.com/u/1173675?v=3","gravatar_id":"","url":"https://api.github.com/users/jlee-r7","html_url":"https://github.com/jlee-r7","followers_url":"https://api.github.com/users/jlee-r7/followers","following_url":"https://api.github.com/users/jlee-r7/following{/other_user}","gists_url":"https://api.github.com/users/jlee-r7/gists{/gist_id}","starred_url":"https://api.github.com/users/jlee-r7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlee-r7/subscriptions","organizations_url":"https://api.github.com/users/jlee-r7/orgs","repos_url":"https://api.github.com/users/jlee-r7/repos","events_url":"https://api.github.com/users/jlee-r7/events{/privacy}","received_events_url":"https://api.github.com/users/jlee-r7/received_events","type":"User","site_admin":false},"body":"Unless I'm misunderstanding how this works, wouldn't this be more simply implemented as an inherited class method?\r\ne.g.:\r\n```ruby\r\ndef self.relative_path_prefix\r\n  \"#{self::MODULE_TYPE_DIRECTORY}\".freeze\r\nend\r\n```","created_at":"2015-03-25T03:33:48Z","updated_at":"2015-03-25T03:33:48Z","html_url":"https://github.com/rapid7/metasploit-cache/pull/9#discussion_r27092974","pull_request_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9","_links":{"self":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/comments/27092974"},"html":{"href":"https://github.com/rapid7/metasploit-cache/pull/9#discussion_r27092974"},"pull_request":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9"}}},"pull_request":{"url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9","id":30668381,"html_url":"https://github.com/rapid7/metasploit-cache/pull/9","diff_url":"https://github.com/rapid7/metasploit-cache/pull/9.diff","patch_url":"https://github.com/rapid7/metasploit-cache/pull/9.patch","issue_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/9","number":9,"state":"open","locked":false,"title":"Metasploit::Cache::Module::Ancestor STI","user":{"login":"limhoff-r7","id":2230482,"avatar_url":"https://avatars.githubusercontent.com/u/2230482?v=3","gravatar_id":"","url":"https://api.github.com/users/limhoff-r7","html_url":"https://github.com/limhoff-r7","followers_url":"https://api.github.com/users/limhoff-r7/followers","following_url":"https://api.github.com/users/limhoff-r7/following{/other_user}","gists_url":"https://api.github.com/users/limhoff-r7/gists{/gist_id}","starred_url":"https://api.github.com/users/limhoff-r7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/limhoff-r7/subscriptions","organizations_url":"https://api.github.com/users/limhoff-r7/orgs","repos_url":"https://api.github.com/users/limhoff-r7/repos","events_url":"https://api.github.com/users/limhoff-r7/events{/privacy}","received_events_url":"https://api.github.com/users/limhoff-r7/received_events","type":"User","site_admin":false},"body":"MSP-12217\r\n\r\nImplemented\r\n\r\n# Verification Steps\r\n- [ ] `rm Gemfile.lock`\r\n- [ ] `bundle install`\r\n- [ ] **NOTE: As metasploit-cache is prerelease, this PR contains changes to pre-existing migration, so you need to rebuild your database**: `rake db:drop db:create db:migrate`\r\n\r\n## Test coverage\r\n- [ ] `rake cucumber spec coverage`\r\n- [ ] VERIFY no failures\r\n- [ ] VERIFY 100% coverage\r\n- [ ] VERIFY all modules from metasploit-framework is tested\r\n\r\n## Documentation\r\n- [ ] `rake yard`\r\n\r\n### Documentation coverage\r\n- [ ] VERIFY no warnings\r\n- [ ] VERIFY no undocumented objects\r\n\r\n## Schema matches design\r\n- [ ] `open docs/images/metasploit-cache.png`\r\n- [ ] Scroll to `Metasploit::Cache::Module::Ancestor`\r\n- [ ] `open doc/Metasploit/Cache.html`\r\n- [ ] Scroll to the Entity-Relationship Diagram\r\n- [ ] VERIFY `Metasploit::Cache::Auxiliary::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Encoder::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Exploit::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Nop::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Payload::Single::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Payload::Stage::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Payload::Stager::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n- [ ] VERIFY `Metasploit::Cache::Post::Ancestor` is grey with a grey arrow connecting to `Metasploit::Cache::Module::Ancestor`, which indicates it is an STI subclass as in the design doc.\r\n\r\n# Post-merge Steps\r\n\r\nPerform these steps prior to pushing to master or the build will be broken on master.\r\n\r\n## Version\r\n- [ ] Edit `lib/metasploit/cache/version.rb`\r\n- [ ] Remove `PRERELEASE` and its comment as `PRERELEASE` is not defined on master.\r\n\r\n## Gem build\r\n- [ ] gem build *.gemspec\r\n- [ ] VERIFY the gem has no '.pre' version suffix.\r\n\r\n## RSpec\r\n- [ ] `rake spec`\r\n- [ ] VERIFY version examples pass without failures\r\n\r\n## Commit & Push\r\n- [ ] `git commit -a`\r\n- [ ] `git push origin master`","created_at":"2015-03-06T18:38:06Z","updated_at":"2015-03-25T03:33:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e0ce20aa35720bbe8d5e4d583e27a698ff317db5","assignee":{"login":"jlee-r7","id":1173675,"avatar_url":"https://avatars.githubusercontent.com/u/1173675?v=3","gravatar_id":"","url":"https://api.github.com/users/jlee-r7","html_url":"https://github.com/jlee-r7","followers_url":"https://api.github.com/users/jlee-r7/followers","following_url":"https://api.github.com/users/jlee-r7/following{/other_user}","gists_url":"https://api.github.com/users/jlee-r7/gists{/gist_id}","starred_url":"https://api.github.com/users/jlee-r7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlee-r7/subscriptions","organizations_url":"https://api.github.com/users/jlee-r7/orgs","repos_url":"https://api.github.com/users/jlee-r7/repos","events_url":"https://api.github.com/users/jlee-r7/events{/privacy}","received_events_url":"https://api.github.com/users/jlee-r7/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9/commits","review_comments_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9/comments","review_comment_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/9/comments","statuses_url":"https://api.github.com/repos/rapid7/metasploit-cache/statuses/4da0516e4abef8cb8e04e823a042b4f7c3813c24","head":{"label":"rapid7:feature/MSP-12217/metasploit-cache-module-ancestor-sti","ref":"feature/MSP-12217/metasploit-cache-module-ancestor-sti","sha":"4da0516e4abef8cb8e04e823a042b4f7c3813c24","user":{"login":"rapid7","id":1013671,"avatar_url":"https://avatars.githubusercontent.com/u/1013671?v=3","gravatar_id":"","url":"https://api.github.com/users/rapid7","html_url":"https://github.com/rapid7","followers_url":"https://api.github.com/users/rapid7/followers","following_url":"https://api.github.com/users/rapid7/following{/other_user}","gists_url":"https://api.github.com/users/rapid7/gists{/gist_id}","starred_url":"https://api.github.com/users/rapid7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rapid7/subscriptions","organizations_url":"https://api.github.com/users/rapid7/orgs","repos_url":"https://api.github.com/users/rapid7/repos","events_url":"https://api.github.com/users/rapid7/events{/privacy}","received_events_url":"https://api.github.com/users/rapid7/received_events","type":"Organization","site_admin":false},"repo":{"id":27936087,"name":"metasploit-cache","full_name":"rapid7/metasploit-cache","owner":{"login":"rapid7","id":1013671,"avatar_url":"https://avatars.githubusercontent.com/u/1013671?v=3","gravatar_id":"","url":"https://api.github.com/users/rapid7","html_url":"https://github.com/rapid7","followers_url":"https://api.github.com/users/rapid7/followers","following_url":"https://api.github.com/users/rapid7/following{/other_user}","gists_url":"https://api.github.com/users/rapid7/gists{/gist_id}","starred_url":"https://api.github.com/users/rapid7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rapid7/subscriptions","organizations_url":"https://api.github.com/users/rapid7/orgs","repos_url":"https://api.github.com/users/rapid7/repos","events_url":"https://api.github.com/users/rapid7/events{/privacy}","received_events_url":"https://api.github.com/users/rapid7/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rapid7/metasploit-cache","description":"","fork":false,"url":"https://api.github.com/repos/rapid7/metasploit-cache","forks_url":"https://api.github.com/repos/rapid7/metasploit-cache/forks","keys_url":"https://api.github.com/repos/rapid7/metasploit-cache/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rapid7/metasploit-cache/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rapid7/metasploit-cache/teams","hooks_url":"https://api.github.com/repos/rapid7/metasploit-cache/hooks","issue_events_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/events{/number}","events_url":"https://api.github.com/repos/rapid7/metasploit-cache/events","assignees_url":"https://api.github.com/repos/rapid7/metasploit-cache/assignees{/user}","branches_url":"https://api.github.com/repos/rapid7/metasploit-cache/branches{/branch}","tags_url":"https://api.github.com/repos/rapid7/metasploit-cache/tags","blobs_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/refs{/sha}","trees_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rapid7/metasploit-cache/statuses/{sha}","languages_url":"https://api.github.com/repos/rapid7/metasploit-cache/languages","stargazers_url":"https://api.github.com/repos/rapid7/metasploit-cache/stargazers","contributors_url":"https://api.github.com/repos/rapid7/metasploit-cache/contributors","subscribers_url":"https://api.github.com/repos/rapid7/metasploit-cache/subscribers","subscription_url":"https://api.github.com/repos/rapid7/metasploit-cache/subscription","commits_url":"https://api.github.com/repos/rapid7/metasploit-cache/commits{/sha}","git_commits_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/commits{/sha}","comments_url":"https://api.github.com/repos/rapid7/metasploit-cache/comments{/number}","issue_comment_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/comments{/number}","contents_url":"https://api.github.com/repos/rapid7/metasploit-cache/contents/{+path}","compare_url":"https://api.github.com/repos/rapid7/metasploit-cache/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rapid7/metasploit-cache/merges","archive_url":"https://api.github.com/repos/rapid7/metasploit-cache/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rapid7/metasploit-cache/downloads","issues_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues{/number}","pulls_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls{/number}","milestones_url":"https://api.github.com/repos/rapid7/metasploit-cache/milestones{/number}","notifications_url":"https://api.github.com/repos/rapid7/metasploit-cache/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rapid7/metasploit-cache/labels{/name}","releases_url":"https://api.github.com/repos/rapid7/metasploit-cache/releases{/id}","created_at":"2014-12-12T20:04:36Z","updated_at":"2015-03-12T15:57:10Z","pushed_at":"2015-03-24T21:09:06Z","git_url":"git://github.com/rapid7/metasploit-cache.git","ssh_url":"git@github.com:rapid7/metasploit-cache.git","clone_url":"https://github.com/rapid7/metasploit-cache.git","svn_url":"https://github.com/rapid7/metasploit-cache","homepage":null,"size":16143,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":24,"forks":0,"open_issues":24,"watchers":0,"default_branch":"master"}},"base":{"label":"rapid7:master","ref":"master","sha":"44513d6b8e8d04d195e5bd236a7d8fa6adae4913","user":{"login":"rapid7","id":1013671,"avatar_url":"https://avatars.githubusercontent.com/u/1013671?v=3","gravatar_id":"","url":"https://api.github.com/users/rapid7","html_url":"https://github.com/rapid7","followers_url":"https://api.github.com/users/rapid7/followers","following_url":"https://api.github.com/users/rapid7/following{/other_user}","gists_url":"https://api.github.com/users/rapid7/gists{/gist_id}","starred_url":"https://api.github.com/users/rapid7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rapid7/subscriptions","organizations_url":"https://api.github.com/users/rapid7/orgs","repos_url":"https://api.github.com/users/rapid7/repos","events_url":"https://api.github.com/users/rapid7/events{/privacy}","received_events_url":"https://api.github.com/users/rapid7/received_events","type":"Organization","site_admin":false},"repo":{"id":27936087,"name":"metasploit-cache","full_name":"rapid7/metasploit-cache","owner":{"login":"rapid7","id":1013671,"avatar_url":"https://avatars.githubusercontent.com/u/1013671?v=3","gravatar_id":"","url":"https://api.github.com/users/rapid7","html_url":"https://github.com/rapid7","followers_url":"https://api.github.com/users/rapid7/followers","following_url":"https://api.github.com/users/rapid7/following{/other_user}","gists_url":"https://api.github.com/users/rapid7/gists{/gist_id}","starred_url":"https://api.github.com/users/rapid7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rapid7/subscriptions","organizations_url":"https://api.github.com/users/rapid7/orgs","repos_url":"https://api.github.com/users/rapid7/repos","events_url":"https://api.github.com/users/rapid7/events{/privacy}","received_events_url":"https://api.github.com/users/rapid7/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rapid7/metasploit-cache","description":"","fork":false,"url":"https://api.github.com/repos/rapid7/metasploit-cache","forks_url":"https://api.github.com/repos/rapid7/metasploit-cache/forks","keys_url":"https://api.github.com/repos/rapid7/metasploit-cache/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rapid7/metasploit-cache/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rapid7/metasploit-cache/teams","hooks_url":"https://api.github.com/repos/rapid7/metasploit-cache/hooks","issue_events_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/events{/number}","events_url":"https://api.github.com/repos/rapid7/metasploit-cache/events","assignees_url":"https://api.github.com/repos/rapid7/metasploit-cache/assignees{/user}","branches_url":"https://api.github.com/repos/rapid7/metasploit-cache/branches{/branch}","tags_url":"https://api.github.com/repos/rapid7/metasploit-cache/tags","blobs_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/refs{/sha}","trees_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rapid7/metasploit-cache/statuses/{sha}","languages_url":"https://api.github.com/repos/rapid7/metasploit-cache/languages","stargazers_url":"https://api.github.com/repos/rapid7/metasploit-cache/stargazers","contributors_url":"https://api.github.com/repos/rapid7/metasploit-cache/contributors","subscribers_url":"https://api.github.com/repos/rapid7/metasploit-cache/subscribers","subscription_url":"https://api.github.com/repos/rapid7/metasploit-cache/subscription","commits_url":"https://api.github.com/repos/rapid7/metasploit-cache/commits{/sha}","git_commits_url":"https://api.github.com/repos/rapid7/metasploit-cache/git/commits{/sha}","comments_url":"https://api.github.com/repos/rapid7/metasploit-cache/comments{/number}","issue_comment_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues/comments{/number}","contents_url":"https://api.github.com/repos/rapid7/metasploit-cache/contents/{+path}","compare_url":"https://api.github.com/repos/rapid7/metasploit-cache/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rapid7/metasploit-cache/merges","archive_url":"https://api.github.com/repos/rapid7/metasploit-cache/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rapid7/metasploit-cache/downloads","issues_url":"https://api.github.com/repos/rapid7/metasploit-cache/issues{/number}","pulls_url":"https://api.github.com/repos/rapid7/metasploit-cache/pulls{/number}","milestones_url":"https://api.github.com/repos/rapid7/metasploit-cache/milestones{/number}","notifications_url":"https://api.github.com/repos/rapid7/metasploit-cache/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rapid7/metasploit-cache/labels{/name}","releases_url":"https://api.github.com/repos/rapid7/metasploit-cache/releases{/id}","created_at":"2014-12-12T20:04:36Z","updated_at":"2015-03-12T15:57:10Z","pushed_at":"2015-03-24T21:09:06Z","git_url":"git://github.com/rapid7/metasploit-cache.git","ssh_url":"git@github.com:rapid7/metasploit-cache.git","clone_url":"https://github.com/rapid7/metasploit-cache.git","svn_url":"https://github.com/rapid7/metasploit-cache","homepage":null,"size":16143,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":24,"forks":0,"open_issues":24,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9"},"html":{"href":"https://github.com/rapid7/metasploit-cache/pull/9"},"issue":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/issues/9"},"comments":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/issues/9/comments"},"review_comments":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9/comments"},"review_comment":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/pulls/9/commits"},"statuses":{"href":"https://api.github.com/repos/rapid7/metasploit-cache/statuses/4da0516e4abef8cb8e04e823a042b4f7c3813c24"}}}}