devver-octopi 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.yardoc +0 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE +20 -0
- data/README.rdoc +144 -0
- data/Rakefile +100 -0
- data/VERSION.yml +5 -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/hash_ext.rb +5 -0
- data/lib/ext/string_ext.rb +5 -0
- data/lib/octopi.rb +136 -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 +132 -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 +123 -0
- data/octopi.gemspec +99 -0
- data/test/api_test.rb +58 -0
- data/test/authenticated_test.rb +39 -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
- metadata +153 -0
data/lib/octopi/blob.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "resource")
|
2
|
+
module Octopi
|
3
|
+
class Blob < Base
|
4
|
+
attr_accessor :text, :data, :name, :sha, :size, :mode, :mime_type
|
5
|
+
include Resource
|
6
|
+
set_resource_name "blob"
|
7
|
+
|
8
|
+
resource_path "/blob/show/:id"
|
9
|
+
|
10
|
+
def self.find(options={})
|
11
|
+
ensure_hash(options)
|
12
|
+
user, repo = gather_details(options)
|
13
|
+
sha = options[:sha]
|
14
|
+
path = options[:path]
|
15
|
+
|
16
|
+
self.validate_args(sha => :sha, user => :user)
|
17
|
+
|
18
|
+
if path
|
19
|
+
super [user, repo, sha, path]
|
20
|
+
else
|
21
|
+
Api.api.get_raw(path_for(:resource), {:id => [user, repo, sha].join('/')})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Branch < Base
|
3
|
+
attr_accessor :name, :sha
|
4
|
+
include Resource
|
5
|
+
set_resource_name "branch", "branches"
|
6
|
+
|
7
|
+
resource_path "/repos/show/:id"
|
8
|
+
|
9
|
+
# Called when we ask for a resource.
|
10
|
+
# Arguments are passed in like [<name>, <sha>]
|
11
|
+
# TODO: Find out why args are doubly nested
|
12
|
+
def initialize(*args)
|
13
|
+
args = args.flatten!
|
14
|
+
self.name = args.first
|
15
|
+
self.sha = args.last
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.all(options={})
|
23
|
+
ensure_hash(options)
|
24
|
+
user, repo = gather_details(options)
|
25
|
+
self.validate_args(user => :user, repo => :repo)
|
26
|
+
BranchSet.new(find_plural([user, repo, 'branches'], :resource)) do |i|
|
27
|
+
{ :name => i.first, :hash => i.last }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "branch")
|
2
|
+
class Octopi::BranchSet < Array
|
3
|
+
include Octopi
|
4
|
+
attr_accessor :user, :repository
|
5
|
+
# Takes a name, returns a branch if it exists
|
6
|
+
def find(name)
|
7
|
+
branch = detect { |b| b.name == name }
|
8
|
+
raise NotFound, Branch if branch.nil?
|
9
|
+
branch
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Comment < Base
|
3
|
+
attr_accessor :content, :author, :title, :updated, :link, :published, :id, :repository
|
4
|
+
include Resource
|
5
|
+
set_resource_name "tree"
|
6
|
+
|
7
|
+
resource_path "/tree/show/:id"
|
8
|
+
|
9
|
+
def self.find(options={})
|
10
|
+
ensure_hash(options)
|
11
|
+
user, repo, branch, sha = gather_details(options)
|
12
|
+
self.validate_args(sha => :sha, user => :user, repo => :repo)
|
13
|
+
super [user, repo, sha]
|
14
|
+
end
|
15
|
+
|
16
|
+
def commit
|
17
|
+
Commit.find(:user => repository.owner, :repo => repository, :sha => /commit\/(.*?)#/.match(link)[1])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Commit < Base
|
3
|
+
include Resource
|
4
|
+
find_path "/commits/list/:query"
|
5
|
+
resource_path "/commits/show/:id"
|
6
|
+
|
7
|
+
attr_accessor :repository, :message, :parents, :author, :url, :id, :committed_date, :authored_date, :tree, :committer, :added, :removed, :modified
|
8
|
+
|
9
|
+
|
10
|
+
# Finds all commits for the given options:
|
11
|
+
#
|
12
|
+
# :repo or :repository or :name - A repository object or the name of a repository
|
13
|
+
# :user - A user object or the login of a user
|
14
|
+
# :branch - A branch object or the name of a branch. Defaults to master.
|
15
|
+
#
|
16
|
+
# Sample usage:
|
17
|
+
#
|
18
|
+
# >> find_all(:user => "fcoury", :repo => "octopi")
|
19
|
+
# => <Latest 30 commits for master branch>
|
20
|
+
#
|
21
|
+
# => find_all(:user => "fcoury", :repo => "octopi", :branch => "lazy") # branch is set to lazy.
|
22
|
+
# => <Latest 30 commits for lazy branch>
|
23
|
+
#
|
24
|
+
def self.find_all(options={})
|
25
|
+
ensure_hash(options)
|
26
|
+
user, repo, branch = gather_details(options)
|
27
|
+
commits = if options[:path]
|
28
|
+
super user, repo.name, branch, options[:path]
|
29
|
+
else
|
30
|
+
super user, repo.name, branch
|
31
|
+
end
|
32
|
+
# Repository is not passed in from the data, set it manually.
|
33
|
+
commits.each { |c| c.repository = repo }
|
34
|
+
commits
|
35
|
+
end
|
36
|
+
|
37
|
+
# Finds all commits for the given options:
|
38
|
+
#
|
39
|
+
# :repo or :repository or :name - A repository object or the name of a repository
|
40
|
+
# :user - A user object or the login of a user
|
41
|
+
# :branch - A branch object or the name of a branch. Defaults to master.
|
42
|
+
# :sha - The commit ID
|
43
|
+
#
|
44
|
+
# Sample usage:
|
45
|
+
#
|
46
|
+
# >> find(:user => "fcoury", :repo => "octopi", :sha => "f6609209c3ac0badd004512d318bfaa508ea10ae")
|
47
|
+
# => <Commit f6609209c3ac0badd004512d318bfaa508ea10ae for branch master>
|
48
|
+
#
|
49
|
+
# >> find(:user => "fcoury", :repo => "octopi", :branch => "lazy", :sha => "f6609209c3ac0badd004512d318bfaa508ea10ae") # branch is set to lazy.
|
50
|
+
# => <Commit f6609209c3ac0badd004512d318bfaa508ea10ae for branch lazy>
|
51
|
+
#
|
52
|
+
def self.find(options={})
|
53
|
+
ensure_hash(options)
|
54
|
+
user, repo, branch, sha = gather_details(options)
|
55
|
+
super [user, repo, sha]
|
56
|
+
end
|
57
|
+
|
58
|
+
def repo_identifier
|
59
|
+
url_parts = url.split('/')
|
60
|
+
if @repository
|
61
|
+
parts = [@repository.owner, @repository.name, url_parts[6]]
|
62
|
+
else
|
63
|
+
parts = [url_parts[3], url_parts[4], url_parts[6]]
|
64
|
+
end
|
65
|
+
|
66
|
+
parts.join('/')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/octopi/error.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Octopi
|
2
|
+
|
3
|
+
class FormatError < StandardError
|
4
|
+
def initialize(f)
|
5
|
+
super("Got unexpected format (got #{f.first} for #{f.last})")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class AuthenticationRequired < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class APIError < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidLogin < StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
class RetryableAPIError < RuntimeError
|
19
|
+
attr_reader :code
|
20
|
+
def initialize(code=nil)
|
21
|
+
@code = code.nil? ? '???' : code
|
22
|
+
@message = "GitHub returned status #{@code}. Retrying request."
|
23
|
+
super @message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ArgumentMustBeHash < Exception; end
|
28
|
+
|
29
|
+
|
30
|
+
class NotFound < Exception
|
31
|
+
def initialize(klass)
|
32
|
+
super "The #{klass.to_s.split("::").last} you were looking for could not be found, or is private."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Octopi
|
2
|
+
class FileObject < Base
|
3
|
+
attr_accessor :name, :sha, :mode, :type
|
4
|
+
|
5
|
+
include Resource
|
6
|
+
set_resource_name "tree"
|
7
|
+
resource_path "/tree/show/:id"
|
8
|
+
|
9
|
+
def self.find(options={})
|
10
|
+
ensure_hash(options)
|
11
|
+
user, repo, branch, sha = gather_details(options)
|
12
|
+
self.validate_args(sha => :sha, user => :user, repo => :repo)
|
13
|
+
super [user, repo, sha]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/octopi/gist.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Octopi
|
2
|
+
# Gist API is... lacking at the moment.
|
3
|
+
# This class serves only as a reminder to implement it later
|
4
|
+
class Gist < Base
|
5
|
+
include HTTParty
|
6
|
+
attr_accessor :description, :repo, :public, :created_at
|
7
|
+
|
8
|
+
include Resource
|
9
|
+
set_resource_name "tree"
|
10
|
+
resource_path ":id"
|
11
|
+
|
12
|
+
def self.base_uri
|
13
|
+
"http://gist.github.com/api/v1/yaml"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find(id)
|
17
|
+
result = get("#{base_uri}/#{id}")
|
18
|
+
# This returns an array of Gists, rather than a single record.
|
19
|
+
new(result["gists"].first)
|
20
|
+
end
|
21
|
+
|
22
|
+
# def files
|
23
|
+
# gists_folder = File.join(ENV['HOME'], ".octopi", "gists")
|
24
|
+
# File.mkdir_p(gists_folder)
|
25
|
+
# `git clone git://`
|
26
|
+
# end
|
27
|
+
end
|
28
|
+
end
|
data/lib/octopi/issue.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Issue < Base
|
3
|
+
include Resource
|
4
|
+
STATES = %w{open closed}
|
5
|
+
|
6
|
+
find_path "/issues/list/:query"
|
7
|
+
resource_path "/issues/show/:id"
|
8
|
+
|
9
|
+
|
10
|
+
attr_accessor :repository, :user, :updated_at, :votes, :number, :title, :body, :closed_at, :labels, :state, :created_at
|
11
|
+
|
12
|
+
def self.search(options={})
|
13
|
+
ensure_hash(options)
|
14
|
+
options[:state] ||= "open"
|
15
|
+
user, repo = gather_details(options)
|
16
|
+
Api.api.get("/issues/search/#{user}/#{repo}/#{options[:state]}/#{options[:keyword]}")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Finds all issues for a given Repository
|
20
|
+
#
|
21
|
+
# You can provide the user and repo parameters as
|
22
|
+
# String or as User and Repository objects. When repo
|
23
|
+
# is provided as a Repository object, user is superfluous.
|
24
|
+
#
|
25
|
+
# If no state is given, "open" is assumed.
|
26
|
+
#
|
27
|
+
# Sample usage:
|
28
|
+
#
|
29
|
+
# find_all(repo, :state => "closed") # repo must be an object
|
30
|
+
# find_all("octopi", :user => "fcoury") # user must be provided
|
31
|
+
# find_all(:user => "fcoury", :repo => "octopi") # state defaults to open
|
32
|
+
#
|
33
|
+
def self.find_all(options={})
|
34
|
+
ensure_hash(options)
|
35
|
+
user, repo = gather_details(options)
|
36
|
+
state = (options[:state] || "open").downcase
|
37
|
+
validate_args(user => :user, repo.name => :repo, state => :state)
|
38
|
+
|
39
|
+
issues = super user, repo.name, state
|
40
|
+
issues.each { |i| i.repository = repo }
|
41
|
+
issues
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO: Make find use hashes like find_all
|
45
|
+
def self.find(options={})
|
46
|
+
ensure_hash(options)
|
47
|
+
# Do not cache issues, as they may change via other means.
|
48
|
+
@cache = false
|
49
|
+
user, repo = gather_details(options)
|
50
|
+
|
51
|
+
validate_args(user => :user, repo => :repo)
|
52
|
+
issue = super user, repo, options[:number]
|
53
|
+
issue.repository = repo
|
54
|
+
issue
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.open(options={})
|
58
|
+
ensure_hash(options)
|
59
|
+
user, repo = gather_details(options)
|
60
|
+
data = Api.api.post("/issues/open/#{user}/#{repo.name}", options[:params])
|
61
|
+
issue = new(data['issue'])
|
62
|
+
issue.repository = repo
|
63
|
+
issue
|
64
|
+
end
|
65
|
+
|
66
|
+
# Re-opens an issue.
|
67
|
+
def reopen!
|
68
|
+
data = Api.api.post(command_path("reopen"))
|
69
|
+
self.state = 'open'
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def close!
|
74
|
+
data = Api.api.post(command_path("close"))
|
75
|
+
self.state = 'closed'
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def save
|
80
|
+
data = Api.api.post(command_path("edit"), { :title => title, :body => body })
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
%w(add remove).each do |oper|
|
85
|
+
define_method("#{oper}_label") do |*labels|
|
86
|
+
labels.each do |label|
|
87
|
+
Api.api.post("#{prefix("label/#{oper}")}/#{label}/#{number}", { :cache => false })
|
88
|
+
if oper == "add"
|
89
|
+
self.labels << label
|
90
|
+
else
|
91
|
+
self.labels -= [label]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def comment(comment)
|
98
|
+
data = Api.api.post(command_path("comment"), { :comment => comment })
|
99
|
+
IssueComment.new(data['comment'])
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def prefix(command)
|
104
|
+
"/issues/#{command}/#{repository.owner}/#{repository.name}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def command_path(command)
|
108
|
+
"#{prefix(command)}/#{number}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "issue")
|
2
|
+
class Octopi::IssueSet < Array
|
3
|
+
include Octopi
|
4
|
+
attr_accessor :user, :repository
|
5
|
+
|
6
|
+
def initialize(array)
|
7
|
+
self.user = array.first.user
|
8
|
+
self.repository = array.first.repository
|
9
|
+
super(array)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(number)
|
13
|
+
issue = detect { |issue| issue.number == number }
|
14
|
+
raise NotFound, Issue if issue.nil?
|
15
|
+
issue
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(options={})
|
19
|
+
Issue.search(options.merge(:user => user, :repo => repository))
|
20
|
+
end
|
21
|
+
end
|
data/lib/octopi/key.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Key < Base
|
3
|
+
include Resource
|
4
|
+
|
5
|
+
attr_accessor :title, :id, :key
|
6
|
+
find_path "/user/keys"
|
7
|
+
|
8
|
+
attr_reader :user
|
9
|
+
|
10
|
+
def self.find_all
|
11
|
+
Api.api.get("user/keys")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.add(options={})
|
15
|
+
ensure_hash(options)
|
16
|
+
Api.api.post("/user/key/add", { :title => options[:title], :key => options[:key], :cache => false })
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove
|
21
|
+
result = Api.api.post "/user/key/remove", { :id => id, :cache => false }
|
22
|
+
keys = result["public_keys"].select { |k| k["title"] == title }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "key")
|
2
|
+
class KeySet < Array
|
3
|
+
include Octopi
|
4
|
+
def find(title)
|
5
|
+
key = detect { |key| key.title == title }
|
6
|
+
raise NotFound, Key if key.nil?
|
7
|
+
key
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(options={})
|
11
|
+
ensure_hash(options)
|
12
|
+
Key.add(options)
|
13
|
+
end
|
14
|
+
end
|
data/lib/octopi/plan.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module Octopi
|
2
|
+
class Repository < Base
|
3
|
+
include Resource
|
4
|
+
attr_accessor :description, :url, :forks, :name, :homepage, :watchers,
|
5
|
+
:owner, :private, :fork, :open_issues, :pledgie, :size,
|
6
|
+
# And now for the stuff returned by search results
|
7
|
+
:actions, :score, :language, :followers, :type, :username,
|
8
|
+
:id, :pushed, :created
|
9
|
+
set_resource_name "repository", "repositories"
|
10
|
+
|
11
|
+
create_path "/repos/create"
|
12
|
+
find_path "/repos/search/:query"
|
13
|
+
resource_path "/repos/show/:id"
|
14
|
+
delete_path "/repos/delete/:id"
|
15
|
+
|
16
|
+
attr_accessor :private
|
17
|
+
|
18
|
+
def owner=(owner)
|
19
|
+
@owner = User.find(owner)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns all branches for the Repository
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# repo = Repository.find("fcoury", "octopi")
|
26
|
+
# repo.branches.each { |r| puts r.name }
|
27
|
+
#
|
28
|
+
def branches
|
29
|
+
Branch.all(:user => self.owner, :repo => self)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns all tags for the Repository
|
33
|
+
#
|
34
|
+
# Example:
|
35
|
+
# repo = Repository.find("fcoury", "octopi")
|
36
|
+
# repo.tags.each { |t| puts t.name }
|
37
|
+
#
|
38
|
+
def tags
|
39
|
+
Tag.all(:user => self.owner, :repo => self)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Returns all the comments for a Repository
|
44
|
+
def comments
|
45
|
+
# We have to specify xmlns as a prefix as the document is namespaced.
|
46
|
+
# Be wary!
|
47
|
+
path = "http#{'s' if private}://github.com/#{owner}/#{name}/comments.atom"
|
48
|
+
xml = Nokogiri::XML(Net::HTTP.get(URI.parse(path)))
|
49
|
+
entries = xml.xpath("//xmlns:entry")
|
50
|
+
comments = []
|
51
|
+
for entry in entries
|
52
|
+
content = entry.xpath("xmlns:content").text.gsub("<", "<").gsub(">", ">")
|
53
|
+
comments << Comment.new(
|
54
|
+
:id => entry.xpath("xmlns:id"),
|
55
|
+
:published => Time.parse(entry.xpath("xmlns:published").text),
|
56
|
+
:updated => Time.parse(entry.xpath("xmlns:updated").text),
|
57
|
+
:link => entry.xpath("xmlns:link/@href").text,
|
58
|
+
:title => entry.xpath("xmlns:title").text,
|
59
|
+
:content => content,
|
60
|
+
:author => entry.xpath("xmlns:author/xmlns:name").text,
|
61
|
+
:repository => self
|
62
|
+
)
|
63
|
+
end
|
64
|
+
comments
|
65
|
+
end
|
66
|
+
|
67
|
+
def clone_url
|
68
|
+
url = private || Api.api.login == self.owner.login ? "git@github.com:" : "git://github.com/"
|
69
|
+
url += "#{self.owner}/#{self.name}.git"
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.find(options={})
|
73
|
+
ensure_hash(options)
|
74
|
+
# Lots of people call the same thing differently.
|
75
|
+
# Can't call gather_details here because this method is used by it internally.
|
76
|
+
repo = options[:repo] || options[:repository] || options[:name]
|
77
|
+
user = options[:user].to_s
|
78
|
+
|
79
|
+
return find_plural(user, :resource) if repo.nil?
|
80
|
+
|
81
|
+
self.validate_args(user => :user, repo => :repo)
|
82
|
+
super user, repo
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.find_all(*args)
|
86
|
+
# FIXME: This should be URI escaped, but have to check how the API
|
87
|
+
# handles escaped characters first.
|
88
|
+
super args.join(" ").gsub(/ /,'+')
|
89
|
+
end
|
90
|
+
|
91
|
+
class << self
|
92
|
+
alias_method :search, :find_all
|
93
|
+
end
|
94
|
+
|
95
|
+
def commits(branch = "master")
|
96
|
+
Commit.find_all(:user => self.owner, :repo => self, :branch => branch)
|
97
|
+
end
|
98
|
+
|
99
|
+
def issues(state = "open")
|
100
|
+
IssueSet.new(Octopi::Issue.find_all(:user => owner, :repository => self))
|
101
|
+
end
|
102
|
+
|
103
|
+
def all_issues
|
104
|
+
Issue::STATES.map{|state| self.issues(state)}.flatten
|
105
|
+
end
|
106
|
+
|
107
|
+
def issue(number)
|
108
|
+
Issue.find(:user => self.owner, :repo => self, :number => number)
|
109
|
+
end
|
110
|
+
|
111
|
+
def collaborators
|
112
|
+
property('collaborators', [self.owner, self.name].join('/')).values.map { |v| User.find(v) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.create(options={})
|
116
|
+
raise AuthenticationRequired, "To create a repository you must be authenticated." if Api.api.read_only?
|
117
|
+
self.validate_args(options[:name] => :repo)
|
118
|
+
new(Api.api.post(path_for(:create), options)["repository"])
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete!
|
122
|
+
raise APIError, "You must be authenticated as the owner of this repository to delete it" if Api.me.login != owner.login
|
123
|
+
token = Api.api.post(self.class.path_for(:delete), :id => self.name)['delete_token']
|
124
|
+
Api.api.post(self.class.path_for(:delete), :id => self.name, :delete_token => token) unless token.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_s
|
128
|
+
name
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|