tpitale-octopi 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.yardoc +0 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE +20 -0
- data/README.markdown +137 -0
- data/Rakefile +91 -0
- data/VERSION.yml +4 -0
- data/contrib/backup.rb +100 -0
- data/examples/authenticated.rb +20 -0
- data/examples/issues.rb +18 -0
- data/examples/overall.rb +50 -0
- data/lib/ext/string_ext.rb +5 -0
- data/lib/octopi.rb +92 -0
- data/lib/octopi/api.rb +213 -0
- data/lib/octopi/base.rb +115 -0
- data/lib/octopi/blob.rb +25 -0
- data/lib/octopi/branch.rb +31 -0
- data/lib/octopi/branch_set.rb +11 -0
- data/lib/octopi/comment.rb +20 -0
- data/lib/octopi/commit.rb +69 -0
- data/lib/octopi/error.rb +35 -0
- data/lib/octopi/file_object.rb +16 -0
- data/lib/octopi/gist.rb +28 -0
- data/lib/octopi/issue.rb +111 -0
- data/lib/octopi/issue_comment.rb +7 -0
- data/lib/octopi/issue_set.rb +21 -0
- data/lib/octopi/key.rb +25 -0
- data/lib/octopi/key_set.rb +14 -0
- data/lib/octopi/plan.rb +5 -0
- data/lib/octopi/repository.rb +130 -0
- data/lib/octopi/repository_set.rb +9 -0
- data/lib/octopi/resource.rb +70 -0
- data/lib/octopi/self.rb +33 -0
- data/lib/octopi/tag.rb +23 -0
- data/lib/octopi/user.rb +131 -0
- data/test/api_test.rb +58 -0
- data/test/authenticated_test.rb +38 -0
- data/test/base_test.rb +20 -0
- data/test/blob_test.rb +23 -0
- data/test/branch_test.rb +20 -0
- data/test/commit_test.rb +82 -0
- data/test/file_object_test.rb +39 -0
- data/test/gist_test.rb +16 -0
- data/test/issue_comment.rb +19 -0
- data/test/issue_set_test.rb +33 -0
- data/test/issue_test.rb +120 -0
- data/test/key_set_test.rb +29 -0
- data/test/key_test.rb +35 -0
- data/test/repository_set_test.rb +23 -0
- data/test/repository_test.rb +151 -0
- data/test/stubs/commits/fcoury/octopi/octopi.rb +818 -0
- data/test/tag_test.rb +20 -0
- data/test/test_helper.rb +246 -0
- data/test/user_test.rb +92 -0
- data/tpitale-octopi.gemspec +99 -0
- metadata +142 -0
data/.gitignore
ADDED
data/.yardoc
ADDED
Binary file
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Felipe Coury
|
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.markdown
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# octopi
|
2
|
+
## Pure Ruby Branch
|
3
|
+
|
4
|
+
Octopi is a Ruby interface to GitHub API v2 (http://develop.github.com).
|
5
|
+
|
6
|
+
To install it as a Gem, just run:
|
7
|
+
|
8
|
+
$ sudo gem install octopi
|
9
|
+
|
10
|
+
Get notifications via Twitter, following @octopi_gem:
|
11
|
+
http://twitter.com/octopi_gem
|
12
|
+
|
13
|
+
## Authenticated Usage
|
14
|
+
|
15
|
+
### Seamless authentication using .gitconfig defaults
|
16
|
+
|
17
|
+
If you have your <tt>~/.gitconfig</tt> file in place, and you have a [github] section (if you don't, take a look at this [GitHub Guides entry][http://github.com/guides/tell-git-your-user-name-and-email-address], you can use seamless authentication using this method:
|
18
|
+
|
19
|
+
authenticated do
|
20
|
+
repo = Repository.find(:name => "api-labrat", :user => "fcoury")
|
21
|
+
end
|
22
|
+
|
23
|
+
### Explicit authentication
|
24
|
+
|
25
|
+
Sometimes, you may not want to get authentication data from _~/.gitconfig_. You want to use GitHub API authenticated as a third party. For this use case, you have a couple of options too.
|
26
|
+
|
27
|
+
**1. Providing login and token inline:**
|
28
|
+
|
29
|
+
authenticated_with :login => "mylogin", :token => "mytoken" do
|
30
|
+
repo = Repository.find(:name => "api-labrat", :user => "fcoury")
|
31
|
+
issue = repo.open_issue :title => "Sample issue",
|
32
|
+
:body => "This issue was opened using GitHub API and Octopi"
|
33
|
+
puts issue.number
|
34
|
+
end
|
35
|
+
|
36
|
+
**2. Providing a YAML file with authentication information:**
|
37
|
+
|
38
|
+
Use the following format:
|
39
|
+
|
40
|
+
#
|
41
|
+
# Octopi GitHub API configuration file
|
42
|
+
#
|
43
|
+
|
44
|
+
# GitHub user login and token
|
45
|
+
login: github-username
|
46
|
+
token: github-token
|
47
|
+
|
48
|
+
# Trace level
|
49
|
+
# Possible values:
|
50
|
+
# false - no tracing, same as if the param is ommited
|
51
|
+
# true - will output each POST or GET operation to the stdout
|
52
|
+
# curl - same as true, but in addition will output the curl equivalent of each command (for debugging)
|
53
|
+
trace: curl
|
54
|
+
|
55
|
+
And change the way you connect to:
|
56
|
+
|
57
|
+
authenticated_with :config => "github.yml" do
|
58
|
+
(...)
|
59
|
+
end
|
60
|
+
|
61
|
+
## Anonymous Usage
|
62
|
+
|
63
|
+
This reflects the usage of the API to retrieve information on a read-only fashion, where the user doesn't have to be authenticated.
|
64
|
+
|
65
|
+
### Users API
|
66
|
+
|
67
|
+
Getting user information
|
68
|
+
|
69
|
+
user = User.find("fcoury")
|
70
|
+
puts "#{user.name} is being followed by #{user.followers.join(", ")} and following #{user.following.join(", ")}"
|
71
|
+
|
72
|
+
The bang methods `followers!` and `following!` retrieves a full User object for each user login returned, so it has to be used carefully.
|
73
|
+
|
74
|
+
user.followers!.each do |u|
|
75
|
+
puts " - #{u.name} (#{u.login}) has #{u.public_repo_count} repo(s)"
|
76
|
+
end
|
77
|
+
|
78
|
+
Searching for user
|
79
|
+
|
80
|
+
users = User.find_all("silva")
|
81
|
+
puts "#{users.size} users found for 'silva':"
|
82
|
+
users.each do |u|
|
83
|
+
puts " - #{u.name}"
|
84
|
+
end
|
85
|
+
|
86
|
+
### Repositories API
|
87
|
+
|
88
|
+
repo = user.repository("octopi") # same as: Repository.find("fcoury", "octopi")
|
89
|
+
puts "Repository: #{repo.name} - #{repo.description} (by #{repo.owner}) - #{repo.url}"
|
90
|
+
puts " Tags: #{repo.tags and repo.tags.map {|t| t.name}.join(", ")}"
|
91
|
+
|
92
|
+
Search:
|
93
|
+
|
94
|
+
repos = Repository.find_all("ruby", "git")
|
95
|
+
puts "#{repos.size} repository(ies) with 'ruby' and 'git':"
|
96
|
+
repos.each do |r|
|
97
|
+
puts " - #{r.name}"
|
98
|
+
end
|
99
|
+
|
100
|
+
Issues API integrated into the Repository object:
|
101
|
+
|
102
|
+
issue = repo.issues.first
|
103
|
+
puts "First open issue: #{issue.number} - #{issue.title} - Created at: #{issue.created_at}"
|
104
|
+
|
105
|
+
Single issue information:
|
106
|
+
|
107
|
+
issue = repo.issue(11)
|
108
|
+
|
109
|
+
Commits API information from a Repository object:
|
110
|
+
|
111
|
+
first_commit = repo.commits.first
|
112
|
+
puts "First commit: #{first_commit.id} - #{first_commit.message} - by #{first_commit.author['name']}"
|
113
|
+
|
114
|
+
Single commit information:
|
115
|
+
|
116
|
+
puts "Diff:"
|
117
|
+
first_commit.details.modified.each {|m| puts "#{m['filename']} DIFF: #{m['diff']}" }
|
118
|
+
|
119
|
+
## Author
|
120
|
+
|
121
|
+
* Felipe Coury - http://felipecoury.com
|
122
|
+
* HasMany.info blog - http://hasmany.info
|
123
|
+
|
124
|
+
## Contributors
|
125
|
+
|
126
|
+
In alphabetical order:
|
127
|
+
|
128
|
+
* Ryan Bigg - http://ryanbigg.net
|
129
|
+
* Brandon Calloway - http://github.com/bcalloway
|
130
|
+
* runpaint - http://github.com/runpaint
|
131
|
+
* Tony Pitale - http://t.pitale.com
|
132
|
+
|
133
|
+
Thanks guys!
|
134
|
+
|
135
|
+
## Copyright
|
136
|
+
|
137
|
+
Copyright (c) 2009 Felipe Coury. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "tpitale-octopi"
|
9
|
+
gem.summary = %Q{A Ruby interface to GitHub API v2}
|
10
|
+
gem.email = "tpitale@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/tpitale/octopi"
|
12
|
+
gem.authors = ["Felipe Coury", "Tony Pitale"]
|
13
|
+
gem.add_dependency('httparty', '>= 0.5.2')
|
14
|
+
gem.add_dependency('api_cache', '>= 0')
|
15
|
+
gem.files.exclude 'test/**/*'
|
16
|
+
gem.files.exclude 'test*'
|
17
|
+
gem.files.exclude 'doc/**/*'
|
18
|
+
gem.files.exclude 'examples/**/*'
|
19
|
+
|
20
|
+
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rake/contrib/sshpublisher'
|
30
|
+
namespace :rubyforge do
|
31
|
+
|
32
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
33
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
34
|
+
|
35
|
+
namespace :release do
|
36
|
+
desc "Publish RDoc to RubyForge."
|
37
|
+
task :docs => [:rdoc] do
|
38
|
+
config = YAML.load(
|
39
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
40
|
+
)
|
41
|
+
|
42
|
+
host = "#{config['username']}@rubyforge.org"
|
43
|
+
remote_dir = "/var/www/gforge-projects/octopi/"
|
44
|
+
local_dir = 'rdoc'
|
45
|
+
|
46
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
rescue LoadError
|
51
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
52
|
+
end
|
53
|
+
|
54
|
+
require 'rake/testtask'
|
55
|
+
Rake::TestTask.new(:test) do |test|
|
56
|
+
test.libs << 'lib' << 'test'
|
57
|
+
test.pattern = 'test/**/*_test.rb'
|
58
|
+
test.verbose = false
|
59
|
+
end
|
60
|
+
|
61
|
+
begin
|
62
|
+
require 'rcov/rcovtask'
|
63
|
+
Rcov::RcovTask.new do |test|
|
64
|
+
test.libs << 'test'
|
65
|
+
test.pattern = 'test/**/*_test.rb'
|
66
|
+
test.verbose = true
|
67
|
+
end
|
68
|
+
rescue LoadError
|
69
|
+
task :rcov do
|
70
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
task :default => :test
|
76
|
+
|
77
|
+
require 'rake/rdoctask'
|
78
|
+
Rake::RDocTask.new do |rdoc|
|
79
|
+
if File.exist?('VERSION.yml')
|
80
|
+
config = YAML.load(File.read('VERSION.yml'))
|
81
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
82
|
+
else
|
83
|
+
version = ""
|
84
|
+
end
|
85
|
+
|
86
|
+
rdoc.rdoc_dir = 'rdoc'
|
87
|
+
rdoc.title = "octopi #{version}"
|
88
|
+
rdoc.rdoc_files.include('README*')
|
89
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
90
|
+
end
|
91
|
+
|
data/VERSION.yml
ADDED
data/contrib/backup.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'octopi')
|
2
|
+
|
3
|
+
USAGE_MSG = <<EOF
|
4
|
+
Usage: #{$0} <user> [<directory>]
|
5
|
+
|
6
|
+
Performs a backup of the named user's GitHub.com data.
|
7
|
+
|
8
|
+
This script will fetch the repositories, along with their metadata and
|
9
|
+
associated issues, for the named user. It will also retrieve the user's
|
10
|
+
profile, and those of his followers. This data will be stored in
|
11
|
+
<directory>/<user>. If a directory is not supplied, ~/.github-backup will be
|
12
|
+
used instead.
|
13
|
+
EOF
|
14
|
+
# TODO: Accept list of targets as argument. The main use case is somebody
|
15
|
+
# wanting all of their repositories checked out, without the performane hit
|
16
|
+
# and clutter of the extraneous metadata,
|
17
|
+
include Octopi
|
18
|
+
|
19
|
+
class Object
|
20
|
+
def to_yaml_file(file)
|
21
|
+
File.open("#{file}.yaml", 'w') do |f|
|
22
|
+
YAML.dump(self, f)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
TARGETS = [:user, :followers, :repositories, :issues]
|
28
|
+
|
29
|
+
@user, @basedir = ARGV
|
30
|
+
raise ArgumentError, USAGE_MSG unless @user
|
31
|
+
@basedir ||= File.expand_path("~/.github-backup")
|
32
|
+
@basedir = File.join(@basedir,@user)
|
33
|
+
TARGETS.map{|k| k.to_s}.each do |dir|
|
34
|
+
dir = File.join(@basedir,dir)
|
35
|
+
FileUtils.mkdir_p(dir) unless File.exists? dir
|
36
|
+
end
|
37
|
+
|
38
|
+
@user = User.find(@user)
|
39
|
+
|
40
|
+
def user
|
41
|
+
puts "* Saving profile"
|
42
|
+
@user.to_yaml_file(@user.login)
|
43
|
+
end
|
44
|
+
|
45
|
+
def followers
|
46
|
+
@user.followers!.each do |follower|
|
47
|
+
puts "* #{follower.login} (#{follower.name})"
|
48
|
+
follower.to_yaml_file(follower.login)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def repositories
|
53
|
+
@user.repositories.each do |repo|
|
54
|
+
puts "* #{repo.name} (#{repo.description})\n---"
|
55
|
+
|
56
|
+
git_dir = File.join(repo.name,'.git')
|
57
|
+
# FIXME: Instead of just checking for a Git directory, we could try `git
|
58
|
+
# pull`, and if that indicates that the repository doesn't exist, `git
|
59
|
+
# clone`
|
60
|
+
if File.exists? git_dir
|
61
|
+
Dir.chdir repo.name do
|
62
|
+
# FIXME: If this fails, try deleting the clone and re-cloning?
|
63
|
+
# FIXME: Confirm this is the best solution as opposed to re-cloning
|
64
|
+
# every time, using `git fetch` or `git clone --mirror`.
|
65
|
+
system("git pull")
|
66
|
+
end
|
67
|
+
else
|
68
|
+
system("git clone #{repo.clone_url}")
|
69
|
+
end
|
70
|
+
repo.to_yaml_file(repo.name)
|
71
|
+
puts
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# TODO: For forked repositories whose parents have issue trackers, get their
|
76
|
+
# issues instead.
|
77
|
+
def issues
|
78
|
+
FileUtils.mkdir_p @user.repositories.map{|r| r.name}
|
79
|
+
@user.repositories.each do |repo|
|
80
|
+
puts "#{repo.name}"
|
81
|
+
Dir.chdir(repo.name) do
|
82
|
+
repo.all_issues.each do |issue|
|
83
|
+
puts "* #{issue.title} [#{issue.state}]"
|
84
|
+
issue.to_yaml_file(issue.number)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
TARGETS.each do |target|
|
91
|
+
target.to_s.each do |title|
|
92
|
+
puts title.capitalize
|
93
|
+
title.length.times {print '#'}
|
94
|
+
end
|
95
|
+
puts
|
96
|
+
Dir.chdir(File.join(@basedir, target.to_s)) do
|
97
|
+
send(target)
|
98
|
+
end
|
99
|
+
puts
|
100
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'octopi')
|
2
|
+
|
3
|
+
include Octopi
|
4
|
+
|
5
|
+
authenticated :trace => "curl" do |g|
|
6
|
+
repo = g.repository("api-labrat")
|
7
|
+
|
8
|
+
issue = repo.open_issue :title => "Sample issue",
|
9
|
+
:body => "This issue was opened using GitHub API and Octopi"
|
10
|
+
puts "Successfully opened issue \##{issue.number}"
|
11
|
+
|
12
|
+
# # labels = issue.add_label "Working", "Todo"
|
13
|
+
# # puts "Labels: #{labels.inspect}"
|
14
|
+
|
15
|
+
issue.close
|
16
|
+
puts "Successfully closed issue \##{issue.number}"
|
17
|
+
|
18
|
+
# labels = issue.remove_label "Todo"
|
19
|
+
# puts "Successfully removed label Todo. Current labels: #{labels.inspect}"
|
20
|
+
end
|
data/examples/issues.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'octopi')
|
2
|
+
|
3
|
+
include Octopi
|
4
|
+
|
5
|
+
user = User.find("fcoury")
|
6
|
+
puts user.name
|
7
|
+
|
8
|
+
repo = user.repository("octopi")
|
9
|
+
puts repo.description
|
10
|
+
|
11
|
+
issue = Issue.find_all(user.login, repo.name).first
|
12
|
+
puts "First open issue: #{issue.number} - #{issue.title} - Created at: #{issue.created_at}"
|
13
|
+
|
14
|
+
issue2 = repo.issues.first
|
15
|
+
puts "First open issue: #{issue.number} - #{issue.title} - Created at: #{issue.created_at}"
|
16
|
+
|
17
|
+
issue3 = repo.issue(issue2.number)
|
18
|
+
puts "First open issue: #{issue.number} - #{issue.title} - Created at: #{issue.created_at}"
|
data/examples/overall.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'octopi')
|
2
|
+
|
3
|
+
include Octopi
|
4
|
+
|
5
|
+
# user information
|
6
|
+
user = User.find("fcoury")
|
7
|
+
puts "#{user.name} is being followed by #{user.followers.join(", ")} and following #{user.following.join(", ")}"
|
8
|
+
|
9
|
+
# the bang version of followers and following
|
10
|
+
# fetches user object for each user, but is
|
11
|
+
# a lot more expensive
|
12
|
+
user.followers!.each do |u|
|
13
|
+
puts " - #{u.name} (#{u.login}) has #{u.public_repo_count} repo(s)"
|
14
|
+
end
|
15
|
+
|
16
|
+
# search user
|
17
|
+
users = User.find_all("silva")
|
18
|
+
puts "#{users.size} users found for 'silva':"
|
19
|
+
users.each do |u|
|
20
|
+
puts " - #{u.name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# repository information
|
24
|
+
# to get all repos for user: user.repositories
|
25
|
+
repo = user.repository("octopi") # same as: Repository.find("fcoury", "octopi")
|
26
|
+
puts "Repository: #{repo.name} - #{repo.description} (by #{repo.owner}) - #{repo.url}"
|
27
|
+
puts " Tags: #{repo.tags and repo.tags.map {|t| t.name}.join(", ")}"
|
28
|
+
|
29
|
+
issue = repo.issues.first
|
30
|
+
puts "Sample open issue: #{issue.number} - #{issue.title} - Created at: #{issue.created_at}"
|
31
|
+
|
32
|
+
# commits of a the repository
|
33
|
+
commit = repo.commits.first
|
34
|
+
puts "Commit: #{commit.id} - #{commit.message} - by #{commit.author['name']}"
|
35
|
+
|
36
|
+
# single commit information
|
37
|
+
# details is the same as: Commit.find(commit)
|
38
|
+
puts "Diff:"
|
39
|
+
commit.modified.each {|m| puts "#{m['filename']} DIFF: #{m['diff']}" }
|
40
|
+
|
41
|
+
# repository search
|
42
|
+
repos = Repository.find_all("ruby", "git")
|
43
|
+
puts "#{repos.size} repository(ies) with 'ruby' and 'git':"
|
44
|
+
repos.each do |r|
|
45
|
+
puts " - #{r.name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# connect "user", "<< token >>" do |github|
|
49
|
+
# puts github.user.name
|
50
|
+
# end
|