github_metadata 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ pkg/*
2
+ tmp/*
3
+ coverage
4
+ rdoc
5
+ *.gem
6
+ .bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in github_metadata.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ github_metadata (0.1.0)
5
+ nokogiri
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.1.2)
11
+ nokogiri (1.4.4)
12
+ rspec (2.4.0)
13
+ rspec-core (~> 2.4.0)
14
+ rspec-expectations (~> 2.4.0)
15
+ rspec-mocks (~> 2.4.0)
16
+ rspec-core (2.4.0)
17
+ rspec-expectations (2.4.0)
18
+ diff-lcs (~> 1.1.2)
19
+ rspec-mocks (2.4.0)
20
+ simplecov (0.3.7)
21
+ simplecov-html (>= 0.3.7)
22
+ simplecov-html (0.3.9)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ github_metadata!
29
+ nokogiri
30
+ rspec (>= 2.0.0)
31
+ simplecov
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Christoph Olszowka
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
1
+ = Github Metadata
2
+
3
+ The Github repository API does not include all the information available on the
4
+ web view of a repo. It also has some trouble reporting correctly the availability of
5
+ a wiki and issues.
6
+
7
+ The github_metadata gem is here to solve this. Currently it gives you, for any github repo:
8
+
9
+ * The availability and (if present) the count of wiki pages
10
+ * The availability and (if present) the count of issues
11
+ * The count of pull requests
12
+ * The list of contributors, with username and real name if available, and thereby also
13
+ the count of contributors
14
+
15
+ By the way: It goes very easy on the github servers in doing so, currently only performing 1 request
16
+ to fetch all of the information per instance.
17
+
18
+ == Usage
19
+
20
+ $ gem install github_metadata
21
+ $ irb
22
+ > require 'github_metadata'
23
+ > m = GithubMetadata.new('aslakhellesoy', 'cucumber')
24
+ > m.has_wiki?
25
+ => true
26
+ > m.wiki_pages
27
+ => 71
28
+ > m.has_issues?
29
+ => false
30
+ > m.contributor_usernames
31
+ => ['aslakhellesoy', ...]
32
+
33
+ Check out the full documentation to see all available methods.
34
+
35
+ == TODO
36
+
37
+ * Better (any?) error handling
38
+ * Merge with my very own first_github_commit gem, which retrieves the very first commit to a repo
39
+
40
+ == Note on Patches/Pull Requests
41
+
42
+ * Fork the project.
43
+ * Make your feature addition or bug fix.
44
+ * Add tests for it. This is important so I don't break it in a
45
+ future version unintentionally.
46
+ * Commit, do not mess with rakefile, version, or history.
47
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
48
+ * Send me a pull request. Bonus points for topic branches.
49
+
50
+ == Copyright
51
+
52
+ Copyright (c) 2011 Christoph Olszowka. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "github_metadata/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "github_metadata"
7
+ s.version = GithubMetadata::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Christoph Olszowka"]
10
+ s.email = ["christoph at olszowka de"]
11
+ s.homepage = ""
12
+ s.summary = %q{Extracts additional information from Github repos that isn't available via API}
13
+ s.description = %q{Extracts additional information like amount of committers, issues and wiki pages from Github repos}
14
+
15
+ s.rubyforge_project = "github_metadata"
16
+
17
+ s.add_dependency 'nokogiri'
18
+ s.add_development_dependency 'rspec', '>= 2.0.0'
19
+ s.add_development_dependency 'simplecov'
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,3 @@
1
+ class GithubMetadata
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,98 @@
1
+ require 'rubygems'
2
+ require 'open-uri'
3
+ require 'nokogiri'
4
+
5
+ # A simple scraper that fetches data from github repos that is not
6
+ # available via the API. See README for an introduction and overview.
7
+ class GithubMetadata
8
+ attr_reader :user, :repo
9
+
10
+ # Object representation of a github contributor
11
+ class Contributor
12
+ attr_reader :username, :realname
13
+ def initialize(username, realname=nil)
14
+ @username, @realname = username, realname
15
+ end
16
+ end
17
+
18
+ def initialize(user, repo)
19
+ @user, @repo = user, repo
20
+ end
21
+
22
+ # Returns an array of GithubMetadata::Contributor instances, one for each
23
+ # contributor listed on the contributors page of github
24
+ def contributors
25
+ load_contributors unless @contributors
26
+ @contributors
27
+ end
28
+
29
+ # Shorthand form for getting an array of all contributor github usernames
30
+ def contributor_usernames
31
+ @contributor_usernames ||= contributors.map(&:username)
32
+ end
33
+
34
+ # Shorthand form for getting an array of all contributor github realnames,
35
+ # with users that don't have a realname specified filtered out
36
+ def contributor_realnames
37
+ @contributor_realnames ||= contributors.map(&:realname).compact
38
+ end
39
+
40
+ # Will return all contributor real names, falling back to the username when
41
+ # real name is not specified
42
+ def contributor_names
43
+ @contributor_names ||= contributors.map {|c| c.realname || c.username }
44
+ end
45
+
46
+ # Returns true when the repo has a wiki
47
+ def has_wiki?
48
+ !!wiki_pages
49
+ end
50
+
51
+ # Returns the amount of wiki pages or nil when no wiki is present
52
+ def wiki_pages
53
+ wiki_link = document.at_css('a[highlight="repo_wiki"]')
54
+ return nil unless wiki_link
55
+ wiki_link.text[/\d+/].to_i
56
+ end
57
+
58
+ # Returns true if the repo has issues enabled
59
+ def has_issues?
60
+ !!issues
61
+ end
62
+
63
+ # Returns issue count or nil if issues are disabled
64
+ def issues
65
+ issue_link = document.at_css('a[highlight="issues"]')
66
+ return nil unless issue_link
67
+ issue_link.text[/\d+/].to_i
68
+ end
69
+
70
+ # Returns amount of pull requests
71
+ def pull_requests
72
+ pull_request_link = document.at_css('a[highlight="repo_pulls"]')
73
+ return nil unless pull_request_link
74
+ pull_request_link.text[/\d+/].to_i
75
+ end
76
+
77
+ private
78
+
79
+ def document
80
+ @document ||= Nokogiri::HTML(open(contributors_url))
81
+ end
82
+
83
+ def contributors_url
84
+ "https://github.com/#{user}/#{repo}/contributors"
85
+ end
86
+
87
+ def load_contributors
88
+ @contributors = document.css('#repos #watchers.members li').map do |contributor|
89
+ line = contributor.text.gsub("\n", '').squeeze(' ').strip.chomp
90
+ username = line.match(/^([^\ ]+)/)[1]
91
+
92
+ name_match = line.match(/\(([^\)]+)\)/)
93
+ real_name = name_match ? name_match[1] : nil
94
+
95
+ GithubMetadata::Contributor.new(username, real_name)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,169 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ describe GithubMetadata do
4
+ context "initialized with aslakhellesoy/cucumber" do
5
+ before(:all) do
6
+ @metadata = GithubMetadata.new('aslakhellesoy', 'cucumber')
7
+ @raw = open("https://github.com/#{@metadata.user}/#{@metadata.repo}/contributors").read
8
+ end
9
+ subject { @metadata }
10
+
11
+ describe '#user' do
12
+ specify do
13
+ subject.user.should == 'aslakhellesoy'
14
+ end
15
+ end
16
+
17
+ describe '#repo' do
18
+ specify do
19
+ subject.repo.should == 'cucumber'
20
+ end
21
+ end
22
+
23
+ specify do
24
+ subject.should have_wiki
25
+ end
26
+
27
+ describe '#wiki_pages' do
28
+ specify do
29
+ expected = @raw.match(/Wiki \((\d+)\)/)[1].to_i
30
+ expected.should be > 50
31
+ subject.wiki_pages.should == expected
32
+ end
33
+ end
34
+
35
+ specify do
36
+ subject.should_not have_issues
37
+ end
38
+
39
+ describe '#issues' do
40
+ specify do
41
+ subject.issues.should == nil
42
+ end
43
+ end
44
+
45
+ describe '#pull_requests' do
46
+ specify do
47
+ expected = @raw.match(/Pull Requests \((\d+)\)/)[1].to_i
48
+ subject.pull_requests.should == expected
49
+ end
50
+ end
51
+
52
+ describe '#contributors' do
53
+ specify do
54
+ expected = @raw.match(/(\d+) contributors/)[1].to_i
55
+ subject.contributors.length.should == expected
56
+ end
57
+ end
58
+
59
+ describe '#contributor_usernames' do
60
+ specify do
61
+ subject.contributor_usernames.should include('aslakhellesoy')
62
+ end
63
+ end
64
+
65
+ describe '#contributor_realnames' do
66
+ specify do
67
+ subject.contributor_realnames.should include('Iain Hecker', 'Elliot Crosby-McCullough', 'Aslak Hellesøy')
68
+ end
69
+
70
+ specify do
71
+ subject.contributor_realnames.length.should < subject.contributor_usernames.length
72
+ end
73
+ end
74
+
75
+ describe '#contributor_names' do
76
+ specify do
77
+ subject.contributor_names.count.should == subject.contributors.count
78
+ end
79
+
80
+ specify do
81
+ subject.contributor_names.should include('Iain Hecker', 'Elliot Crosby-McCullough', 'Aslak Hellesøy')
82
+ end
83
+
84
+ specify do
85
+ subject.contributor_names.should include('marocchino')
86
+ end
87
+ end
88
+ end
89
+
90
+ context "initialized with colszowka/simplecov" do
91
+ before(:all) do
92
+ @metadata = GithubMetadata.new('colszowka', 'simplecov')
93
+ @raw = open("https://github.com/#{@metadata.user}/#{@metadata.repo}/contributors").read
94
+ end
95
+ subject { @metadata }
96
+
97
+ describe '#user' do
98
+ specify do
99
+ subject.user.should == 'colszowka'
100
+ end
101
+ end
102
+
103
+ describe '#repo' do
104
+ specify do
105
+ subject.repo.should == 'simplecov'
106
+ end
107
+ end
108
+
109
+ specify do
110
+ subject.should_not have_wiki
111
+ end
112
+
113
+ describe '#wiki_pages' do
114
+ specify do
115
+ subject.wiki_pages.should be nil
116
+ end
117
+ end
118
+
119
+ specify do
120
+ subject.should have_issues
121
+ end
122
+
123
+ describe '#issues' do
124
+ specify do
125
+ expected = @raw.match(/Issues \((\d+)\)/)[1].to_i
126
+ subject.issues.should == expected
127
+ end
128
+ end
129
+
130
+ describe '#pull_requests' do
131
+ specify do
132
+ subject.pull_requests.should be 0
133
+ end
134
+ end
135
+
136
+ describe '#contributors' do
137
+ specify do
138
+ expected = @raw.match(/(\d+) contributors/)[1].to_i
139
+ subject.contributors.length.should be expected
140
+ end
141
+
142
+ it 'should be all instance of Contributor' do
143
+ subject.contributors.should be_all {|c| c.instance_of?(GithubMetadata::Contributor)}
144
+ end
145
+ end
146
+
147
+ describe '#contributor_usernames' do
148
+ specify do
149
+ subject.contributor_usernames.should include('colszowka')
150
+ end
151
+ end
152
+
153
+ describe '#contributor_realnames' do
154
+ specify do
155
+ subject.contributor_realnames.should include('Christoph Olszowka')
156
+ end
157
+ end
158
+
159
+ describe '#contributor_names' do
160
+ specify do
161
+ subject.contributor_names.count.should be subject.contributors.count
162
+ end
163
+ specify do
164
+ subject.contributor_names.should include('Christoph Olszowka')
165
+ end
166
+ end
167
+ end
168
+
169
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ require 'github_metadata'
6
+
7
+ RSpec.configure do |config|
8
+ # some (optional) config here
9
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github_metadata
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Christoph Olszowka
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-04 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: nokogiri
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: rspec
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 2
43
+ - 0
44
+ - 0
45
+ version: 2.0.0
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: simplecov
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id003
61
+ description: Extracts additional information like amount of committers, issues and wiki pages from Github repos
62
+ email:
63
+ - christoph at olszowka de
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ files:
71
+ - .gitignore
72
+ - .rspec
73
+ - Gemfile
74
+ - Gemfile.lock
75
+ - LICENSE
76
+ - README.rdoc
77
+ - Rakefile
78
+ - github_metadata.gemspec
79
+ - lib/github_metadata.rb
80
+ - lib/github_metadata/version.rb
81
+ - spec/github_metadata_spec.rb
82
+ - spec/spec_helper.rb
83
+ has_rdoc: true
84
+ homepage: ""
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options: []
89
+
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project: github_metadata
111
+ rubygems_version: 1.3.7
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Extracts additional information from Github repos that isn't available via API
115
+ test_files:
116
+ - spec/github_metadata_spec.rb
117
+ - spec/spec_helper.rb