fcoury-octopi 0.0.9 → 0.0.11
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 +3 -0
- data/README.rdoc +4 -1
- data/Rakefile +83 -0
- data/VERSION.yml +1 -1
- data/contrib/backup.rb +100 -0
- data/examples/authenticated.rb +20 -0
- data/examples/github.yml.example +14 -0
- data/examples/issues.rb +18 -0
- data/examples/overall.rb +50 -0
- data/lib/octopi/base.rb +7 -5
- data/lib/octopi/commit.rb +3 -2
- data/lib/octopi/issue.rb +5 -1
- data/lib/octopi/repository.rb +38 -6
- data/lib/octopi/resource.rb +10 -1
- data/lib/octopi/user.rb +32 -2
- data/lib/octopi.rb +39 -14
- data/octopi.gemspec +66 -0
- data/test/octopi_test.rb +2 -2
- metadata +21 -11
data/.gitignore
ADDED
data/README.rdoc
CHANGED
@@ -4,8 +4,11 @@ Octopi is a Ruby interface to GitHub API v2 (http://develop.github.com).
|
|
4
4
|
|
5
5
|
To install it as a Gem, just run:
|
6
6
|
|
7
|
-
$ sudo gem install
|
7
|
+
$ sudo gem install octopi
|
8
8
|
|
9
|
+
Get notifications via Twitter, following @octopi_gem:
|
10
|
+
http://twitter.com/octopi_gem
|
11
|
+
|
9
12
|
== Authenticated Usage
|
10
13
|
|
11
14
|
=== Seamless authentication using .gitconfig defaults
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "octopi"
|
8
|
+
gem.summary = %Q{A Ruby interface to GitHub API v2}
|
9
|
+
gem.email = "felipe.coury@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/fcoury/octopi"
|
11
|
+
gem.authors = ["Felipe Coury"]
|
12
|
+
gem.rubyforge_project = "octopi"
|
13
|
+
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'rake/contrib/sshpublisher'
|
22
|
+
namespace :rubyforge do
|
23
|
+
|
24
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
25
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
26
|
+
|
27
|
+
namespace :release do
|
28
|
+
desc "Publish RDoc to RubyForge."
|
29
|
+
task :docs => [:rdoc] do
|
30
|
+
config = YAML.load(
|
31
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
32
|
+
)
|
33
|
+
|
34
|
+
host = "#{config['username']}@rubyforge.org"
|
35
|
+
remote_dir = "/var/www/gforge-projects/octopi/"
|
36
|
+
local_dir = 'rdoc'
|
37
|
+
|
38
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
44
|
+
end
|
45
|
+
|
46
|
+
require 'rake/testtask'
|
47
|
+
Rake::TestTask.new(:test) do |test|
|
48
|
+
test.libs << 'lib' << 'test'
|
49
|
+
test.pattern = 'test/**/*_test.rb'
|
50
|
+
test.verbose = false
|
51
|
+
end
|
52
|
+
|
53
|
+
begin
|
54
|
+
require 'rcov/rcovtask'
|
55
|
+
Rcov::RcovTask.new do |test|
|
56
|
+
test.libs << 'test'
|
57
|
+
test.pattern = 'test/**/*_test.rb'
|
58
|
+
test.verbose = true
|
59
|
+
end
|
60
|
+
rescue LoadError
|
61
|
+
task :rcov do
|
62
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
task :default => :test
|
68
|
+
|
69
|
+
require 'rake/rdoctask'
|
70
|
+
Rake::RDocTask.new do |rdoc|
|
71
|
+
if File.exist?('VERSION.yml')
|
72
|
+
config = YAML.load(File.read('VERSION.yml'))
|
73
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
74
|
+
else
|
75
|
+
version = ""
|
76
|
+
end
|
77
|
+
|
78
|
+
rdoc.rdoc_dir = 'rdoc'
|
79
|
+
rdoc.title = "octopi #{version}"
|
80
|
+
rdoc.rdoc_files.include('README*')
|
81
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
82
|
+
end
|
83
|
+
|
data/VERSION.yml
CHANGED
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#
|
2
|
+
# Octopi GitHub API configuration file
|
3
|
+
#
|
4
|
+
|
5
|
+
# GitHub user login and token
|
6
|
+
login: github-username
|
7
|
+
token: github-token
|
8
|
+
|
9
|
+
# Trace level
|
10
|
+
# Possible values:
|
11
|
+
# false - no tracing, same as if the param is ommited
|
12
|
+
# true - will output each POST or GET operation to the stdout
|
13
|
+
# curl - same as true, but in addition will output the curl equivalent of each command (for debugging)
|
14
|
+
trace: curl
|
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.details.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
|
data/lib/octopi/base.rb
CHANGED
@@ -9,7 +9,7 @@ module Octopi
|
|
9
9
|
:repo => {
|
10
10
|
# FIXME: API currently chokes on repository names containing periods,
|
11
11
|
# but presumably this will be fixed.
|
12
|
-
:pat => /^[
|
12
|
+
:pat => /^[A-Za-z0-9_\.-]+$/,
|
13
13
|
:msg => "%s is an invalid repository name"},
|
14
14
|
:user => {
|
15
15
|
:pat => /^[A-Za-z0-9_\.-]+$/,
|
@@ -67,20 +67,22 @@ module Octopi
|
|
67
67
|
def self.extract_user_repository(*args)
|
68
68
|
opts = args.last.is_a?(Hash) ? args.pop : {}
|
69
69
|
if opts.empty?
|
70
|
-
|
71
|
-
|
70
|
+
if args.length > 1
|
71
|
+
repo, user = *args
|
72
|
+
else
|
73
|
+
repo = args.pop
|
74
|
+
end
|
72
75
|
else
|
73
76
|
opts[:repo] = opts[:repository] if opts[:repository]
|
74
77
|
repo = args.pop || opts[:repo]
|
75
78
|
user = opts[:user]
|
76
79
|
end
|
77
80
|
|
78
|
-
user
|
81
|
+
user = repo.owner if repo.is_a? Repository
|
79
82
|
|
80
83
|
if repo.is_a?(String) and !user
|
81
84
|
raise "Need user argument when repository is identified by name"
|
82
85
|
end
|
83
|
-
|
84
86
|
ret = extract_names(user, repo)
|
85
87
|
ret << opts
|
86
88
|
ret
|
data/lib/octopi/commit.rb
CHANGED
@@ -21,13 +21,14 @@ module Octopi
|
|
21
21
|
# find_all(:user => "fcoury", :repo => "octopi") # branch defaults to master
|
22
22
|
#
|
23
23
|
def self.find_all(*args)
|
24
|
+
api = args.last.is_a?(Api) ? args.pop : ANONYMOUS_API
|
24
25
|
repo = args.first
|
25
26
|
user ||= repo.owner if repo.is_a? Repository
|
26
27
|
user, repo_name, opts = extract_user_repository(*args)
|
27
28
|
self.validate_args(user => :user, repo_name => :repo)
|
28
29
|
branch = opts[:branch] || "master"
|
29
|
-
|
30
|
-
commits = super user, repo_name, branch
|
30
|
+
api = ANONYMOUS_API if repo.is_a?(Repository) && !repo.private
|
31
|
+
commits = super user, repo_name, branch, api
|
31
32
|
commits.each { |c| c.repository = repo } if repo.is_a? Repository
|
32
33
|
commits
|
33
34
|
end
|
data/lib/octopi/issue.rb
CHANGED
@@ -78,13 +78,17 @@ module Octopi
|
|
78
78
|
data = @api.post(command_path("edit"), { :title => self.title, :body => self.body })
|
79
79
|
end
|
80
80
|
|
81
|
-
%
|
81
|
+
%w(add remove).each do |oper|
|
82
82
|
define_method("#{oper}_label") do |*labels|
|
83
83
|
labels.each do |label|
|
84
84
|
@api.post("#{prefix("label/#{oper}")}/#{label}/#{number}")
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
88
|
+
|
89
|
+
def comment(comment)
|
90
|
+
@api.post(command_path("comment"), { :comment => comment })
|
91
|
+
end
|
88
92
|
|
89
93
|
private
|
90
94
|
def prefix(command)
|
data/lib/octopi/repository.rb
CHANGED
@@ -3,27 +3,45 @@ module Octopi
|
|
3
3
|
include Resource
|
4
4
|
set_resource_name "repository", "repositories"
|
5
5
|
|
6
|
+
create_path "/repos/create"
|
6
7
|
find_path "/repos/search/:query"
|
7
8
|
resource_path "/repos/show/:id"
|
9
|
+
delete_path "/repos/delete/:id"
|
8
10
|
|
11
|
+
attr_accessor :private
|
12
|
+
|
13
|
+
# Returns all branches for the Repository
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# repo = Repository.find("fcoury", "octopi")
|
17
|
+
# repo.branches.each { |r| puts r.name }
|
18
|
+
#
|
9
19
|
def branches
|
10
20
|
Branch.find(self.owner, self.name)
|
11
21
|
end
|
12
22
|
|
23
|
+
# Returns all tags for the Repository
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
# repo = Repository.find("fcoury", "octopi")
|
27
|
+
# repo.tags.each { |t| puts t.name }
|
28
|
+
#
|
13
29
|
def tags
|
14
30
|
Tag.find(self.owner, self.name)
|
15
31
|
end
|
16
32
|
|
17
33
|
def clone_url
|
18
|
-
|
19
|
-
|
20
|
-
|
34
|
+
if private? || api.login == self.owner
|
35
|
+
"git@github.com:#{self.owner}/#{self.name}.git"
|
36
|
+
else
|
37
|
+
"git://github.com/#{self.owner}/#{self.name}.git"
|
38
|
+
end
|
21
39
|
end
|
22
40
|
|
23
|
-
def self.find_by_user(user)
|
41
|
+
def self.find_by_user(user, api = ANONYMOUS_API)
|
24
42
|
user = user.login if user.is_a? User
|
25
43
|
self.validate_args(user => :user)
|
26
|
-
find_plural(user, :resource)
|
44
|
+
find_plural(user, :resource, api)
|
27
45
|
end
|
28
46
|
|
29
47
|
def self.find(*args)
|
@@ -56,7 +74,8 @@ module Octopi
|
|
56
74
|
end
|
57
75
|
|
58
76
|
def commits(branch = "master")
|
59
|
-
|
77
|
+
api = self.api || ANONYMOUS_API
|
78
|
+
Commit.find_all(self, {:branch => branch}, api)
|
60
79
|
end
|
61
80
|
|
62
81
|
def issues(state = "open")
|
@@ -74,6 +93,19 @@ module Octopi
|
|
74
93
|
def collaborators
|
75
94
|
property('collaborators', [self.owner,self.name].join('/')).values
|
76
95
|
end
|
96
|
+
|
97
|
+
def self.create(owner, name, opts = {})
|
98
|
+
api = owner.is_a?(User) ? owner.api : ANONYMOUS_API
|
99
|
+
raise APIError, "To create a repository you must be authenticated." if api.read_only?
|
100
|
+
self.validate_args(name => :repo)
|
101
|
+
api.post(path_for(:create), opts.merge(:name => name))
|
102
|
+
self.find(owner, name, api)
|
103
|
+
end
|
104
|
+
|
105
|
+
def delete
|
106
|
+
token = @api.post(self.class.path_for(:delete), :id => self.name)['delete_token']
|
107
|
+
@api.post(self.class.path_for(:delete), :id => self.name, :delete_token => token) unless token.nil?
|
108
|
+
end
|
77
109
|
|
78
110
|
end
|
79
111
|
end
|
data/lib/octopi/resource.rb
CHANGED
@@ -20,6 +20,10 @@ module Octopi
|
|
20
20
|
@resource_name[key]
|
21
21
|
end
|
22
22
|
|
23
|
+
def create_path(path)
|
24
|
+
(@path_spec||={})[:create] = path
|
25
|
+
end
|
26
|
+
|
23
27
|
def find_path(path)
|
24
28
|
(@path_spec||={})[:find] = path
|
25
29
|
end
|
@@ -27,6 +31,10 @@ module Octopi
|
|
27
31
|
def resource_path(path)
|
28
32
|
(@path_spec||={})[:resource] = path
|
29
33
|
end
|
34
|
+
|
35
|
+
def delete_path(path)
|
36
|
+
(@path_spec||={})[:delete] = path
|
37
|
+
end
|
30
38
|
|
31
39
|
def find(*args)
|
32
40
|
api = args.last.is_a?(Api) ? args.pop : ANONYMOUS_API
|
@@ -42,7 +50,8 @@ module Octopi
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def find_all(*s)
|
45
|
-
|
53
|
+
api = s.last.is_a?(Api) ? s.pop : ANONYMOUS_API
|
54
|
+
find_plural(s, :find, api)
|
46
55
|
end
|
47
56
|
|
48
57
|
def find_plural(s, path, api = ANONYMOUS_API)
|
data/lib/octopi/user.rb
CHANGED
@@ -4,26 +4,54 @@ module Octopi
|
|
4
4
|
|
5
5
|
find_path "/user/search/:query"
|
6
6
|
resource_path "/user/show/:id"
|
7
|
-
|
7
|
+
|
8
|
+
# Finds a single user identified by the given username
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# user = User.find("fcoury")
|
13
|
+
# puts user.login # should return 'fcoury'
|
8
14
|
def self.find(username)
|
9
15
|
self.validate_args(username => :user)
|
10
16
|
super username
|
11
17
|
end
|
12
18
|
|
19
|
+
# Finds all users whose username matches a given string
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# User.find_all("oe") # Matches joe, moe and monroe
|
24
|
+
#
|
13
25
|
def self.find_all(username)
|
14
26
|
self.validate_args(username => :user)
|
15
27
|
super username
|
16
28
|
end
|
17
29
|
|
30
|
+
# Returns a collection of Repository objects, containing
|
31
|
+
# all repositories of the user.
|
32
|
+
#
|
33
|
+
# If user is the current authenticated user, some
|
34
|
+
# additional information will be provided for the
|
35
|
+
# Repositories.
|
18
36
|
def repositories
|
19
|
-
|
37
|
+
api = self.api || ANONYMOUS_API
|
38
|
+
Repository.find_by_user(login,api)
|
20
39
|
end
|
21
40
|
|
41
|
+
# Searches for user Repository identified by
|
42
|
+
# name
|
22
43
|
def repository(name)
|
23
44
|
self.class.validate_args(name => :repo)
|
24
45
|
Repository.find(login, name)
|
25
46
|
end
|
26
47
|
|
48
|
+
def create_repository(name, opts = {})
|
49
|
+
self.class.validate_args(name => :repo)
|
50
|
+
Repository.create(self, name, opts)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adds an SSH Public Key to the user. Requires
|
54
|
+
# authentication.
|
27
55
|
def add_key(title, key)
|
28
56
|
raise APIError,
|
29
57
|
"To add a key, you must be authenticated" if @api.read_only?
|
@@ -35,6 +63,8 @@ module Octopi
|
|
35
63
|
Key.new(@api, key_params.first, self)
|
36
64
|
end
|
37
65
|
|
66
|
+
# Returns a list of Key objects containing all SSH Public Keys this user
|
67
|
+
# currently has. Requires authentication.
|
38
68
|
def keys
|
39
69
|
raise APIError,
|
40
70
|
"To add a key, you must be authenticated" if @api.read_only?
|
data/lib/octopi.rb
CHANGED
@@ -4,16 +4,13 @@ require 'yaml'
|
|
4
4
|
require 'pp'
|
5
5
|
|
6
6
|
module Octopi
|
7
|
-
class Api; end
|
8
|
-
ANONYMOUS_API = Api.new
|
9
|
-
|
10
7
|
def authenticated(*args, &block)
|
11
8
|
opts = args.last.is_a?(Hash) ? args.last : {}
|
12
9
|
config = read_gitconfig
|
13
10
|
login = config["github"]["user"]
|
14
11
|
token = config["github"]["token"]
|
15
12
|
|
16
|
-
api =
|
13
|
+
api = AuthApi.new(login, token)
|
17
14
|
api.trace_level = opts[:trace]
|
18
15
|
|
19
16
|
puts "=> Trace on: #{api.trace_level}" if api.trace_level
|
@@ -36,7 +33,7 @@ module Octopi
|
|
36
33
|
|
37
34
|
puts "=> Trace on: #{trace}" if trace
|
38
35
|
|
39
|
-
api =
|
36
|
+
api = AuthApi.new(login, token)
|
40
37
|
api.trace_level = trace if trace
|
41
38
|
yield api
|
42
39
|
end
|
@@ -59,7 +56,6 @@ module Octopi
|
|
59
56
|
end
|
60
57
|
|
61
58
|
class Api
|
62
|
-
include HTTParty
|
63
59
|
CONTENT_TYPE = {
|
64
60
|
'yaml' => 'application/x-yaml',
|
65
61
|
'json' => 'application/json',
|
@@ -67,9 +63,7 @@ module Octopi
|
|
67
63
|
}
|
68
64
|
RETRYABLE_STATUS = [403]
|
69
65
|
MAX_RETRIES = 10
|
70
|
-
|
71
|
-
base_uri "http://github.com/api/v2"
|
72
|
-
|
66
|
+
|
73
67
|
attr_accessor :format, :login, :token, :trace_level, :read_only
|
74
68
|
|
75
69
|
def initialize(login = nil, token = nil, format = "yaml")
|
@@ -111,6 +105,11 @@ module Octopi
|
|
111
105
|
end
|
112
106
|
alias_method :repo, :repository
|
113
107
|
|
108
|
+
def commits(repo,opts={})
|
109
|
+
branch = opts[:branch] || "master"
|
110
|
+
commits = Commit.find_all(repo, branch, self)
|
111
|
+
end
|
112
|
+
|
114
113
|
def save(resource_path, data)
|
115
114
|
traslate resource_path, data
|
116
115
|
#still can't figure out on what format values are expected
|
@@ -140,6 +139,7 @@ module Octopi
|
|
140
139
|
if @@retries < MAX_RETRIES
|
141
140
|
$stderr.puts e.message
|
142
141
|
@@retries += 1
|
142
|
+
sleep 6
|
143
143
|
retry
|
144
144
|
else
|
145
145
|
raise APIError, "GitHub returned status #{e.code}, despite" +
|
@@ -149,10 +149,23 @@ module Octopi
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def post(path, params = {}, format = "yaml")
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
152
|
+
@@retries = 0
|
153
|
+
begin
|
154
|
+
trace "POST", "/#{format}#{path}", params
|
155
|
+
submit(path, params, format) do |path, params, format|
|
156
|
+
resp = self.class.post "/#{format}#{path}", :query => params
|
157
|
+
resp
|
158
|
+
end
|
159
|
+
rescue RetryableAPIError => e
|
160
|
+
if @@retries < MAX_RETRIES
|
161
|
+
$stderr.puts e.message
|
162
|
+
@@retries += 1
|
163
|
+
sleep 6
|
164
|
+
retry
|
165
|
+
else
|
166
|
+
raise APIError, "GitHub returned status #{e.code}, despite" +
|
167
|
+
" repeating the request #{MAX_RETRIES} times. Giving up."
|
168
|
+
end
|
156
169
|
end
|
157
170
|
end
|
158
171
|
|
@@ -203,8 +216,20 @@ module Octopi
|
|
203
216
|
puts "#{oper}: #{url}#{par_str}"
|
204
217
|
end
|
205
218
|
end
|
219
|
+
|
220
|
+
class AuthApi < Api
|
221
|
+
include HTTParty
|
222
|
+
base_uri "https://github.com/api/v2"
|
223
|
+
end
|
224
|
+
|
225
|
+
class AnonymousApi < Api
|
226
|
+
include HTTParty
|
227
|
+
base_uri "http://github.com/api/v2"
|
228
|
+
end
|
229
|
+
|
230
|
+
ANONYMOUS_API = AnonymousApi.new
|
206
231
|
|
207
232
|
%w{error base resource user tag repository issue file_object blob key commit branch}.
|
208
233
|
each{|f| require "#{File.dirname(__FILE__)}/octopi/#{f}"}
|
209
234
|
|
210
|
-
end
|
235
|
+
end
|
data/octopi.gemspec
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{octopi}
|
5
|
+
s.version = "0.0.11"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Felipe Coury"]
|
9
|
+
s.date = %q{2009-06-02}
|
10
|
+
s.email = %q{felipe.coury@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"LICENSE",
|
18
|
+
"README.rdoc",
|
19
|
+
"Rakefile",
|
20
|
+
"VERSION.yml",
|
21
|
+
"contrib/backup.rb",
|
22
|
+
"examples/authenticated.rb",
|
23
|
+
"examples/github.yml.example",
|
24
|
+
"examples/issues.rb",
|
25
|
+
"examples/overall.rb",
|
26
|
+
"lib/octopi.rb",
|
27
|
+
"lib/octopi/base.rb",
|
28
|
+
"lib/octopi/blob.rb",
|
29
|
+
"lib/octopi/branch.rb",
|
30
|
+
"lib/octopi/commit.rb",
|
31
|
+
"lib/octopi/error.rb",
|
32
|
+
"lib/octopi/file_object.rb",
|
33
|
+
"lib/octopi/issue.rb",
|
34
|
+
"lib/octopi/key.rb",
|
35
|
+
"lib/octopi/repository.rb",
|
36
|
+
"lib/octopi/resource.rb",
|
37
|
+
"lib/octopi/tag.rb",
|
38
|
+
"lib/octopi/user.rb",
|
39
|
+
"octopi.gemspec",
|
40
|
+
"test/octopi_test.rb",
|
41
|
+
"test/test_helper.rb"
|
42
|
+
]
|
43
|
+
s.homepage = %q{http://github.com/fcoury/octopi}
|
44
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubyforge_project = %q{octopi}
|
47
|
+
s.rubygems_version = %q{1.3.3}
|
48
|
+
s.summary = %q{A Ruby interface to GitHub API v2}
|
49
|
+
s.test_files = [
|
50
|
+
"test/octopi_test.rb",
|
51
|
+
"test/test_helper.rb",
|
52
|
+
"examples/authenticated.rb",
|
53
|
+
"examples/issues.rb",
|
54
|
+
"examples/overall.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
62
|
+
else
|
63
|
+
end
|
64
|
+
else
|
65
|
+
end
|
66
|
+
end
|
data/test/octopi_test.rb
CHANGED
@@ -7,8 +7,8 @@ class OctopiTest < Test::Unit::TestCase
|
|
7
7
|
|
8
8
|
def assert_find_all(cls, check_method, repo, user)
|
9
9
|
repo_method = cls.resource_name(:plural)
|
10
|
-
|
11
|
-
item1 = cls.find_all(user.login
|
10
|
+
|
11
|
+
item1 = cls.find_all(repo.name,user.login).first
|
12
12
|
item2 = cls.find_all(repo).first
|
13
13
|
item3 = repo.send(repo_method).first
|
14
14
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fcoury-octopi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felipe Coury
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-06-02 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -20,12 +20,20 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
|
-
- README.rdoc
|
24
23
|
- LICENSE
|
24
|
+
- README.rdoc
|
25
25
|
files:
|
26
|
+
- .gitignore
|
27
|
+
- LICENSE
|
26
28
|
- README.rdoc
|
29
|
+
- Rakefile
|
27
30
|
- VERSION.yml
|
28
|
-
-
|
31
|
+
- contrib/backup.rb
|
32
|
+
- examples/authenticated.rb
|
33
|
+
- examples/github.yml.example
|
34
|
+
- examples/issues.rb
|
35
|
+
- examples/overall.rb
|
36
|
+
- lib/octopi.rb
|
29
37
|
- lib/octopi/base.rb
|
30
38
|
- lib/octopi/blob.rb
|
31
39
|
- lib/octopi/branch.rb
|
@@ -38,15 +46,13 @@ files:
|
|
38
46
|
- lib/octopi/resource.rb
|
39
47
|
- lib/octopi/tag.rb
|
40
48
|
- lib/octopi/user.rb
|
41
|
-
-
|
49
|
+
- octopi.gemspec
|
42
50
|
- test/octopi_test.rb
|
43
51
|
- test/test_helper.rb
|
44
|
-
|
45
|
-
has_rdoc: true
|
52
|
+
has_rdoc: false
|
46
53
|
homepage: http://github.com/fcoury/octopi
|
47
54
|
post_install_message:
|
48
55
|
rdoc_options:
|
49
|
-
- --inline-source
|
50
56
|
- --charset=UTF-8
|
51
57
|
require_paths:
|
52
58
|
- lib
|
@@ -64,10 +70,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
70
|
version:
|
65
71
|
requirements: []
|
66
72
|
|
67
|
-
rubyforge_project:
|
73
|
+
rubyforge_project: octopi
|
68
74
|
rubygems_version: 1.2.0
|
69
75
|
signing_key:
|
70
76
|
specification_version: 3
|
71
77
|
summary: A Ruby interface to GitHub API v2
|
72
|
-
test_files:
|
73
|
-
|
78
|
+
test_files:
|
79
|
+
- test/octopi_test.rb
|
80
|
+
- test/test_helper.rb
|
81
|
+
- examples/authenticated.rb
|
82
|
+
- examples/issues.rb
|
83
|
+
- examples/overall.rb
|